mirror of
https://github.com/TelegramMessenger/Telegram-iOS.git
synced 2026-07-06 03:33:41 +02:00
Merge commit '90a4dbe855'
This commit is contained in:
commit
87e914d1d2
87 changed files with 3466 additions and 2436 deletions
Binary file not shown.
Binary file not shown.
|
|
@ -15669,3 +15669,55 @@ Error: %8$@";
|
|||
|
||||
"Conversation.Summary.Limit.Title" = "AI Summary";
|
||||
"Conversation.Summary.Limit.Text" = "Summarize large messages with AI – unlimited with Telegram Premium.";
|
||||
|
||||
"Appearance.SendWithCmdEnter" = "Send Messages with ⌘+Enter";
|
||||
|
||||
"Notification.StarsGift.Crafted" = "You crafted a new unique collectible";
|
||||
|
||||
"Gift.Craft.Title" = "Craft Gift";
|
||||
"Gift.Craft.Description" = "Add up to **4 gifts** to craft new\n**$ %@**.\n\nIf crafting fails, all used gifts\nwill be lost.";
|
||||
"Gift.Craft.ViewVariants" = "View all new variants";
|
||||
"Gift.Craft.BackdropTooltip" = "There's **%1$@%** chance the crafted gift will have **%2$@** backdrop.";
|
||||
"Gift.Craft.SymbolTooltip" = "There's **%1$@%** chance the crafted gift will have **%2$@** symbol.";
|
||||
"Gift.Craft.SuccessChanceSuffix" = "% Success Chance";
|
||||
"Gift.Craft.Craft" = "Craft %@";
|
||||
"Gift.Craft.Crafting.Title" = "Crafting";
|
||||
"Gift.Craft.Crafting.Description" = "If crafting fails, all used gifts\nwill be lost.";
|
||||
"Gift.Craft.Crafting.SuccessChance" = "%@% Success Chance";
|
||||
"Gift.Craft.CraftingFailed.Title" = "Crafting Failed";
|
||||
"Gift.Craft.CraftingFailed.Text_1" = "This crafting attempt was unsuccessful.\n**%@ gift** was lost.";
|
||||
"Gift.Craft.CraftingFailed.Text_any" = "This crafting attempt was unsuccessful.\n**%@ gifts** were lost.";
|
||||
"Gift.Craft.CraftingFailed.CraftAnotherGift" = "Craft Another Gift";
|
||||
"Gift.Craft.Info.Title" = "Gift Crafting";
|
||||
"Gift.Craft.Info.Description" = "Turn your gifts intro rare, epic,\nuncommon and legendary versions.";
|
||||
"Gift.Craft.Info.Paragraph1.Title" = "Get Rare Models";
|
||||
"Gift.Craft.Info.Paragraph1.Text" = "Select up to 4 gifts to craft a new exclusive model.";
|
||||
"Gift.Craft.Info.Paragraph2.Title" = "Maximize Chances";
|
||||
"Gift.Craft.Info.Paragraph2.Text" = "Combine more gifts to increase your odds of success.";
|
||||
"Gift.Craft.Info.Paragraph3.Title" = "Affect the Result";
|
||||
"Gift.Craft.Info.Paragraph3.Text" = "Use gifts with the same attribute to boost it's chance.";
|
||||
"Gift.Craft.Info.Understood" = "Understood";
|
||||
"Gift.Craft.Unavailable.Title" = "Try Later";
|
||||
"Gift.Craft.Unavailable.Text" = "You will be able to craft this gift on %@.";
|
||||
|
||||
"Gift.Craft.Select.Title" = "Select Gifts";
|
||||
"Gift.Craft.Select.YourGifts" = "Your Gifts";
|
||||
"Gift.Craft.Select.SaleGifts" = "Suitable Gifts On Sale";
|
||||
"Gift.Craft.Select.NoGiftsFromCollection" = "You don't have other gifts\nfrom this collection";
|
||||
|
||||
"Gift.Attribute.Rare" = "rare";
|
||||
"Gift.Attribute.Legendary" = "legendary";
|
||||
"Gift.Attribute.Epic" = "epic";
|
||||
"Gift.Attribute.Uncommon" = "uncommon";
|
||||
|
||||
"SocksProxySetup.CheckStatus" = "Check Status";
|
||||
"SocksProxySetup.Warning.Title" = "Warning";
|
||||
"SocksProxySetup.Warning.Text" = "This action will expose your IP address to the admin of the proxy server.";
|
||||
"SocksProxySetup.Warning.Proceed" = "Proceed";
|
||||
|
||||
"Gift.Variants.CollectionInfo.CraftableModel_1" = "**%@** craftable model";
|
||||
"Gift.Variants.CollectionInfo.CraftableModel_any" = "**%@** craftable models";
|
||||
"Gift.Variants.ViewCraftableModels" = "View Craftable Models";
|
||||
"Gift.Variants.ViewPrimaryModels" = "View Primary Models";
|
||||
|
||||
"Gift.View.OnSale" = "On sale for %@";
|
||||
|
|
|
|||
|
|
@ -1300,6 +1300,7 @@ public protocol SharedAccountContext: AnyObject {
|
|||
var currentMediaInputSettings: Atomic<MediaInputSettings> { get }
|
||||
var currentStickerSettings: Atomic<StickerSettings> { get }
|
||||
var currentMediaDisplaySettings: Atomic<MediaDisplaySettings> { get }
|
||||
var currentChatSettings: Atomic<ChatSettings> { get }
|
||||
|
||||
var energyUsageSettings: EnergyUsageSettings { get }
|
||||
|
||||
|
|
@ -1459,15 +1460,15 @@ public protocol SharedAccountContext: AnyObject {
|
|||
func makeGiftViewScreen(context: AccountContext, message: EngineMessage, shareStory: ((StarGift.UniqueGift) -> Void)?) -> ViewController
|
||||
func makeGiftViewScreen(context: AccountContext, gift: StarGift.UniqueGift, shareStory: ((StarGift.UniqueGift) -> Void)?, openChatTheme: (() -> Void)?, dismissed: (() -> Void)?) -> ViewController
|
||||
func makeGiftWearPreviewScreen(context: AccountContext, gift: StarGift, attributes: [StarGift.UniqueGift.Attribute]?) -> ViewController
|
||||
func makeGiftUpgradePreviewScreen(context: AccountContext, attributes: [StarGift.UniqueGift.Attribute], peerName: String) -> ViewController
|
||||
func makeGiftUpgradePreviewScreen(context: AccountContext, gift: StarGift.Gift, attributes: [StarGift.UniqueGift.Attribute], peerName: String) -> ViewController
|
||||
func makeGiftAuctionInfoScreen(context: AccountContext, auctionContext: GiftAuctionContext, completion: (() -> Void)?) -> ViewController
|
||||
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, peerId: EnginePeer.Id?, completion: @escaping (Signal<[GiftAuctionAcquiredGift], NoError>, [StarGift.UniqueGift.Attribute]?) -> Void) -> ViewController
|
||||
func makeGiftAuctionActiveBidsScreen(context: AccountContext) -> 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, onlyCrafted: Bool, attributes: [StarGift.UniqueGift.Attribute], selectedAttributes: [StarGift.UniqueGift.Attribute]?, focusedAttribute: StarGift.UniqueGift.Attribute?) -> ViewController
|
||||
func makeGiftUpgradeVariantsScreen(context: AccountContext, gift: StarGift, crafted: Bool, 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 makeGiftCraftScreen(context: AccountContext, gift: StarGift.UniqueGift) -> ViewController
|
||||
func makeGiftCraftScreen(context: AccountContext, gift: StarGift.UniqueGift, profileGiftsContext: ProfileGiftsContext?) -> ViewController
|
||||
func makeGiftDemoScreen(context: AccountContext) -> ViewController
|
||||
func makeStorySharingScreen(context: AccountContext, subject: StorySharingSubject, parentController: ViewController) -> ViewController
|
||||
func makeContentReportScreen(context: AccountContext, subject: ReportContentSubject, forceDark: Bool, present: @escaping (ViewController) -> Void, completion: @escaping () -> Void, requestSelectMessages: ((String, Data, String?) -> Void)?)
|
||||
|
|
|
|||
|
|
@ -1489,7 +1489,7 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
|||
}
|
||||
}
|
||||
|
||||
public func chatInputTextNodeShouldReturn() -> Bool {
|
||||
public func chatInputTextNodeShouldReturn(modifierFlags: UIKeyModifierFlags) -> Bool {
|
||||
if self.actionButtons.sendButton.supernode != nil && !self.actionButtons.sendButton.isHidden && !self.actionButtons.sendButton.alpha.isZero {
|
||||
self.sendButtonPressed()
|
||||
}
|
||||
|
|
@ -1497,7 +1497,7 @@ public class AttachmentTextInputPanelNode: ASDisplayNode, TGCaptionPanelView, AS
|
|||
}
|
||||
|
||||
@objc public func editableTextNodeShouldReturn(_ editableTextNode: ASEditableTextNode) -> Bool {
|
||||
return self.chatInputTextNodeShouldReturn()
|
||||
return self.chatInputTextNodeShouldReturn(modifierFlags: [])
|
||||
}
|
||||
|
||||
private func applyUpdateSendButtonIcon() {
|
||||
|
|
|
|||
|
|
@ -19,6 +19,7 @@ public final class ResizableSheetComponentEnvironment: Equatable {
|
|||
public let screenSize: CGSize
|
||||
public let regularMetricsSize: CGSize?
|
||||
public let dismiss: (Bool) -> Void
|
||||
public let boundsUpdated: ActionSlot<CGRect>
|
||||
|
||||
public init(
|
||||
theme: PresentationTheme,
|
||||
|
|
@ -30,7 +31,8 @@ public final class ResizableSheetComponentEnvironment: Equatable {
|
|||
isCentered: Bool,
|
||||
screenSize: CGSize,
|
||||
regularMetricsSize: CGSize?,
|
||||
dismiss: @escaping (Bool) -> Void
|
||||
dismiss: @escaping (Bool) -> Void,
|
||||
boundsUpdated: ActionSlot<CGRect> = ActionSlot<CGRect>()
|
||||
) {
|
||||
self.theme = theme
|
||||
self.statusBarHeight = statusBarHeight
|
||||
|
|
@ -42,6 +44,7 @@ public final class ResizableSheetComponentEnvironment: Equatable {
|
|||
self.screenSize = screenSize
|
||||
self.regularMetricsSize = regularMetricsSize
|
||||
self.dismiss = dismiss
|
||||
self.boundsUpdated = boundsUpdated
|
||||
}
|
||||
|
||||
public static func ==(lhs: ResizableSheetComponentEnvironment, rhs: ResizableSheetComponentEnvironment) -> Bool {
|
||||
|
|
@ -179,7 +182,7 @@ public final class ResizableSheetComponent<ChildEnvironmentType: Sendable & Equa
|
|||
}
|
||||
}
|
||||
|
||||
public final class View: UIView, UIScrollViewDelegate, ComponentTaggedView {
|
||||
public final class View: UIView, UIScrollViewDelegate, ComponentTaggedView, UIGestureRecognizerDelegate {
|
||||
public final class Tag {
|
||||
public init() {
|
||||
}
|
||||
|
|
@ -213,6 +216,10 @@ public final class ResizableSheetComponent<ChildEnvironmentType: Sendable & Equa
|
|||
private let backgroundHandleView: UIImageView
|
||||
|
||||
private var ignoreScrolling: Bool = false
|
||||
private var isDismissingInteractively: Bool = false
|
||||
private var dismissTranslation: CGFloat = 0.0
|
||||
private var dismissStartTranslation: CGFloat?
|
||||
private var dismissPanGesture: UIPanGestureRecognizer?
|
||||
|
||||
private var component: ResizableSheetComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
|
|
@ -284,6 +291,12 @@ public final class ResizableSheetComponent<ChildEnvironmentType: Sendable & Equa
|
|||
self.containerView.addSubview(self.bottomContainer)
|
||||
|
||||
self.dimView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapGesture(_:))))
|
||||
|
||||
let dismissPanGesture = UIPanGestureRecognizer(target: self, action: #selector(self.dismissPanGesture(_:)))
|
||||
dismissPanGesture.maximumNumberOfTouches = 1
|
||||
dismissPanGesture.delegate = self
|
||||
self.addGestureRecognizer(dismissPanGesture)
|
||||
self.dismissPanGesture = dismissPanGesture
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
|
|
@ -314,6 +327,26 @@ public final class ResizableSheetComponent<ChildEnvironmentType: Sendable & Equa
|
|||
return result
|
||||
}
|
||||
|
||||
override public func gestureRecognizerShouldBegin(_ gestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
if gestureRecognizer === self.dismissPanGesture {
|
||||
let pan = gestureRecognizer as! UIPanGestureRecognizer
|
||||
let velocity = pan.velocity(in: self)
|
||||
if abs(velocity.y) <= abs(velocity.x) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public func gestureRecognizer(_ gestureRecognizer: UIGestureRecognizer, shouldRecognizeSimultaneouslyWith otherGestureRecognizer: UIGestureRecognizer) -> Bool {
|
||||
if gestureRecognizer === self.dismissPanGesture {
|
||||
if otherGestureRecognizer === self.scrollView.panGestureRecognizer {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@objc private func dimTapGesture(_ recognizer: UITapGestureRecognizer) {
|
||||
if case .ended = recognizer.state {
|
||||
self.dismissAnimated()
|
||||
|
|
@ -328,6 +361,79 @@ public final class ResizableSheetComponent<ChildEnvironmentType: Sendable & Equa
|
|||
environment.dismiss(true)
|
||||
}
|
||||
|
||||
private func updateDismissTranslation(_ translation: CGFloat) {
|
||||
self.dismissTranslation = translation
|
||||
self.updateScrolling(transition: .immediate)
|
||||
|
||||
let maxAlphaDistance = max(1.0, self.bounds.height * 0.9)
|
||||
let alpha = 1.0 - min(1.0, translation / maxAlphaDistance)
|
||||
self.dimView.alpha = alpha
|
||||
}
|
||||
|
||||
private func resetDismissTranslation(animated: Bool) {
|
||||
self.dismissTranslation = 0.0
|
||||
if animated {
|
||||
let transition: ComponentTransition = .easeInOut(duration: 0.2)
|
||||
transition.setAlpha(view: self.dimView, alpha: 1.0)
|
||||
self.updateScrolling(transition: transition)
|
||||
} else {
|
||||
self.dimView.alpha = 1.0
|
||||
self.updateScrolling(transition: .immediate)
|
||||
}
|
||||
}
|
||||
|
||||
@objc private func dismissPanGesture(_ recognizer: UIPanGestureRecognizer) {
|
||||
guard let component = self.component else {
|
||||
return
|
||||
}
|
||||
|
||||
let translation = recognizer.translation(in: self)
|
||||
switch recognizer.state {
|
||||
case .began:
|
||||
self.dismissStartTranslation = nil
|
||||
case .changed:
|
||||
let shouldStartDismiss = self.scrollView.contentOffset.y <= 0.0 && translation.y > 0.0
|
||||
if shouldStartDismiss {
|
||||
if !self.isDismissingInteractively {
|
||||
self.isDismissingInteractively = true
|
||||
self.dismissStartTranslation = translation.y
|
||||
self.scrollView.isScrollEnabled = false
|
||||
}
|
||||
|
||||
let start = self.dismissStartTranslation ?? translation.y
|
||||
let dismissOffset = max(0.0, translation.y - start)
|
||||
self.scrollView.contentOffset = .zero
|
||||
self.updateDismissTranslation(dismissOffset)
|
||||
} else if self.isDismissingInteractively {
|
||||
let start = self.dismissStartTranslation ?? translation.y
|
||||
let dismissOffset = max(0.0, translation.y - start)
|
||||
self.updateDismissTranslation(dismissOffset)
|
||||
}
|
||||
case .ended, .cancelled:
|
||||
if self.isDismissingInteractively {
|
||||
let velocityY = recognizer.velocity(in: self).y
|
||||
let currentOffset = self.dismissTranslation
|
||||
let threshold = min(180.0, self.bounds.height * 0.25)
|
||||
let shouldDismiss = currentOffset > threshold || velocityY > 1000.0
|
||||
|
||||
self.isDismissingInteractively = false
|
||||
self.scrollView.isScrollEnabled = !component.isFullscreen
|
||||
|
||||
if shouldDismiss {
|
||||
let animateOffset = self.bounds.height - self.backgroundLayer.frame.minY
|
||||
let initialVelocity = animateOffset > 0.0 ? max(0.0, velocityY) / animateOffset : 0.0
|
||||
self.animateOut(initialVelocity: initialVelocity, completion: { [weak self] in
|
||||
self?.environment?.dismiss(false)
|
||||
})
|
||||
} else {
|
||||
self.resetDismissTranslation(animated: true)
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
private func updateScrolling(transition: ComponentTransition) {
|
||||
guard let itemLayout = self.itemLayout, let component = self.component else {
|
||||
return
|
||||
|
|
@ -356,6 +462,7 @@ public final class ResizableSheetComponent<ChildEnvironmentType: Sendable & Equa
|
|||
var containerTransform = CATransform3DIdentity
|
||||
containerTransform = CATransform3DTranslate(containerTransform, 0.0, scaledTranslation, 0.0)
|
||||
containerTransform = CATransform3DScale(containerTransform, scale, scale, scale)
|
||||
containerTransform = CATransform3DTranslate(containerTransform, 0.0, self.dismissTranslation, 0.0)
|
||||
transition.setTransform(view: self.containerView, transform: containerTransform)
|
||||
transition.setCornerRadius(layer: self.containerView.layer, cornerRadius: scaledCornerRadius)
|
||||
|
||||
|
|
@ -363,8 +470,10 @@ public final class ResizableSheetComponent<ChildEnvironmentType: Sendable & Equa
|
|||
transition.setBounds(view: self.scrollView, bounds: CGRect(origin: .zero, size: self.scrollView.bounds.size))
|
||||
self.scrollView.isScrollEnabled = false
|
||||
} else {
|
||||
self.scrollView.isScrollEnabled = true
|
||||
self.scrollView.isScrollEnabled = !self.isDismissingInteractively
|
||||
}
|
||||
|
||||
self.environment?.boundsUpdated.invoke(self.scrollView.bounds)
|
||||
}
|
||||
|
||||
private var didPlayAppearanceAnimation = false
|
||||
|
|
@ -379,16 +488,28 @@ public final class ResizableSheetComponent<ChildEnvironmentType: Sendable & Equa
|
|||
self.bottomContainer.layer.animatePosition(from: CGPoint(x: 0.0, y: animateOffset), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
||||
}
|
||||
|
||||
func animateOut(completion: @escaping () -> Void) {
|
||||
func animateOut(initialVelocity: CGFloat? = nil, completion: @escaping () -> Void) {
|
||||
let animateOffset: CGFloat = self.bounds.height - self.backgroundLayer.frame.minY
|
||||
|
||||
self.dimView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false)
|
||||
self.scrollContentClippingView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: animateOffset), duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, additive: true, completion: { _ in
|
||||
completion()
|
||||
})
|
||||
self.backgroundLayer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: animateOffset), duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, additive: true)
|
||||
self.navigationBarContainer.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: animateOffset), duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, additive: true)
|
||||
self.bottomContainer.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: animateOffset), duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, additive: true)
|
||||
self.dimView.layer.animateAlpha(from: self.dimView.alpha, to: 0.0, duration: 0.3, removeOnCompletion: false)
|
||||
if let initialVelocity = initialVelocity {
|
||||
let transition = ContainedViewLayoutTransition.animated(duration: 0.35, curve: .customSpring(damping: 124.0, initialVelocity: initialVelocity))
|
||||
|
||||
transition.updatePosition(layer: self.scrollContentClippingView.layer, position: CGPoint(x: self.scrollContentClippingView.layer.position.x, y: self.scrollContentClippingView.layer.position.y + animateOffset), completion: { _ in
|
||||
completion()
|
||||
})
|
||||
transition.updatePosition(layer: self.backgroundLayer, position: CGPoint(x: self.backgroundLayer.position.x, y: self.backgroundLayer.position.y + animateOffset))
|
||||
transition.updatePosition(layer: self.navigationBarContainer.layer, position: CGPoint(x: self.navigationBarContainer.layer.position.x, y: self.navigationBarContainer.layer.position.y + animateOffset))
|
||||
transition.updatePosition(layer: self.bottomContainer.layer, position: CGPoint(x: self.bottomContainer.layer.position.x, y: self.bottomContainer.layer.position.y + animateOffset))
|
||||
} else {
|
||||
let duration: Double = 0.25
|
||||
self.scrollContentClippingView.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: animateOffset), duration: duration, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, additive: true, completion: { _ in
|
||||
completion()
|
||||
})
|
||||
self.backgroundLayer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: animateOffset), duration: duration, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, additive: true)
|
||||
self.navigationBarContainer.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: animateOffset), duration: duration, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, additive: true)
|
||||
self.bottomContainer.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: animateOffset), duration: duration, timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, removeOnCompletion: false, additive: true)
|
||||
}
|
||||
}
|
||||
|
||||
func update(component: ResizableSheetComponent<ChildEnvironmentType>, availableSize: CGSize, state: EmptyComponentState, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
|
||||
|
|
@ -530,6 +651,11 @@ public final class ResizableSheetComponent<ChildEnvironmentType: Sendable & Equa
|
|||
if let view = leftItemView.view {
|
||||
if view.superview == nil {
|
||||
self.navigationBarContainer.addSubview(view)
|
||||
|
||||
if !transition.animation.isImmediate {
|
||||
view.layer.animateScale(from: 0.01, to: 1.0, duration: 0.25)
|
||||
view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
||||
}
|
||||
}
|
||||
leftItemTransition.setFrame(view: view, frame: leftItemFrame)
|
||||
}
|
||||
|
|
@ -587,17 +713,19 @@ public final class ResizableSheetComponent<ChildEnvironmentType: Sendable & Equa
|
|||
}
|
||||
|
||||
if let bottomItem = component.bottomItem {
|
||||
var bottomItemTransition = transition
|
||||
let bottomItemView: ComponentView<Empty>
|
||||
if let current = self.bottomItemView {
|
||||
bottomItemView = current
|
||||
} else {
|
||||
bottomItemTransition = .immediate
|
||||
bottomItemView = ComponentView<Empty>()
|
||||
self.bottomItemView = bottomItemView
|
||||
}
|
||||
|
||||
let bottomInsets = ContainerViewLayout.concentricInsets(bottomInset: sheetEnvironment.safeInsets.bottom, innerDiameter: 52.0, sideInset: 30.0)
|
||||
let bottomItemSize = bottomItemView.update(
|
||||
transition: transition,
|
||||
transition: bottomItemTransition,
|
||||
component: bottomItem,
|
||||
environment: {},
|
||||
containerSize: CGSize(width: containerSize.width - bottomInsets.left - bottomInsets.right, height: 52.0)
|
||||
|
|
@ -606,12 +734,24 @@ public final class ResizableSheetComponent<ChildEnvironmentType: Sendable & Equa
|
|||
if let view = bottomItemView.view {
|
||||
if view.superview == nil {
|
||||
self.bottomContainer.addSubview(view)
|
||||
|
||||
if !transition.animation.isImmediate {
|
||||
view.layer.animateScale(from: 0.01, to: 1.0, duration: 0.25)
|
||||
view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
||||
}
|
||||
}
|
||||
transition.setFrame(view: view, frame: bottomItemFrame)
|
||||
bottomItemTransition.setFrame(view: view, frame: bottomItemFrame)
|
||||
}
|
||||
} else if let bottomItemView = self.bottomItemView {
|
||||
self.bottomItemView = nil
|
||||
bottomItemView.view?.removeFromSuperview()
|
||||
if !transition.animation.isImmediate {
|
||||
bottomItemView.view?.layer.animateScale(from: 1.0, to: 0.01, duration: 0.25, removeOnCompletion: false)
|
||||
bottomItemView.view?.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in
|
||||
bottomItemView.view?.removeFromSuperview()
|
||||
})
|
||||
} else {
|
||||
bottomItemView.view?.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
|
||||
let bottomEdgeEffectFrame = CGRect(origin: CGPoint(x: rawSideInset, y: availableSize.height - edgeEffectHeight), size: CGSize(width: fillingSize, height: edgeEffectHeight))
|
||||
|
|
|
|||
|
|
@ -191,9 +191,7 @@ public final class SheetComponent<ChildEnvironmentType: Sendable & Equatable>: C
|
|||
|
||||
self.scrollView = ScrollView()
|
||||
self.scrollView.delaysContentTouches = false
|
||||
if #available(iOSApplicationExtension 11.0, iOS 11.0, *) {
|
||||
self.scrollView.contentInsetAdjustmentBehavior = .never
|
||||
}
|
||||
self.scrollView.contentInsetAdjustmentBehavior = .never
|
||||
self.scrollView.showsVerticalScrollIndicator = false
|
||||
self.scrollView.showsHorizontalScrollIndicator = false
|
||||
self.scrollView.alwaysBounceVertical = true
|
||||
|
|
@ -351,6 +349,9 @@ public final class SheetComponent<ChildEnvironmentType: Sendable & Equatable>: C
|
|||
})
|
||||
} else {
|
||||
var targetOffset: CGFloat = self.scrollView.contentSize.height + abs(contentView.frame.minY) + 6.0
|
||||
if self.currentHasInputHeight {
|
||||
targetOffset += 330.0
|
||||
}
|
||||
if self.isCentered {
|
||||
targetOffset = self.frame.height + self.scrollView.frame.height * 0.5
|
||||
}
|
||||
|
|
|
|||
|
|
@ -89,6 +89,7 @@ public class ItemListActionItem: ListViewItem, ItemListItem {
|
|||
|
||||
public class ItemListActionItemNode: ListViewItemNode, ItemListItemNode {
|
||||
private let backgroundNode: ASDisplayNode
|
||||
private let highlightNode: ASDisplayNode
|
||||
private let topStripeNode: ASDisplayNode
|
||||
private let bottomStripeNode: ASDisplayNode
|
||||
private let highlightedBackgroundNode: ASDisplayNode
|
||||
|
|
@ -107,7 +108,10 @@ public class ItemListActionItemNode: ListViewItemNode, ItemListItemNode {
|
|||
public init() {
|
||||
self.backgroundNode = ASDisplayNode()
|
||||
self.backgroundNode.isLayerBacked = true
|
||||
self.backgroundNode.backgroundColor = .white
|
||||
|
||||
self.highlightNode = ASDisplayNode()
|
||||
self.highlightNode.isLayerBacked = true
|
||||
|
||||
self.maskNode = ASImageNode()
|
||||
self.topStripeNode = ASDisplayNode()
|
||||
self.topStripeNode.isLayerBacked = true
|
||||
|
|
@ -132,6 +136,20 @@ public class ItemListActionItemNode: ListViewItemNode, ItemListItemNode {
|
|||
self.addSubnode(self.activateArea)
|
||||
}
|
||||
|
||||
public func displayHighlight() {
|
||||
if self.backgroundNode.supernode != nil {
|
||||
self.insertSubnode(self.highlightNode, aboveSubnode: self.backgroundNode)
|
||||
} else {
|
||||
self.insertSubnode(self.highlightNode, at: 0)
|
||||
}
|
||||
|
||||
Queue.mainQueue().after(1.2, {
|
||||
self.highlightNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { _ in
|
||||
self.highlightNode.removeFromSupernode()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
public func asyncLayout() -> (_ item: ItemListActionItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, (Bool) -> Void) {
|
||||
let makeTitleLayout = TextNode.asyncLayout(self.titleNode)
|
||||
|
||||
|
|
@ -211,6 +229,7 @@ public class ItemListActionItemNode: ListViewItemNode, ItemListItemNode {
|
|||
strongSelf.bottomStripeNode.backgroundColor = itemSeparatorColor
|
||||
strongSelf.backgroundNode.backgroundColor = itemBackgroundColor
|
||||
strongSelf.highlightedBackgroundNode.backgroundColor = item.presentationData.theme.list.itemHighlightedBackgroundColor
|
||||
strongSelf.highlightNode.backgroundColor = item.presentationData.theme.list.itemSearchHighlightColor
|
||||
}
|
||||
|
||||
let _ = titleApply()
|
||||
|
|
@ -273,6 +292,7 @@ public class ItemListActionItemNode: ListViewItemNode, ItemListItemNode {
|
|||
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.presentationData.theme, top: hasTopCorners, bottom: hasBottomCorners, glass: item.systemStyle == .glass) : nil
|
||||
|
||||
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
|
||||
strongSelf.highlightNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
|
||||
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
|
||||
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: separatorHeight))
|
||||
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: params.width - params.rightInset - bottomStripeInset - separatorRightInset, height: separatorHeight))
|
||||
|
|
|
|||
|
|
@ -139,6 +139,7 @@ swift_library(
|
|||
"//submodules/TelegramUI/Components/Settings/PeerSelectionScreen",
|
||||
"//submodules/TelegramUI/Components/ListSectionComponent",
|
||||
"//submodules/TelegramUI/Components/ListActionItemComponent",
|
||||
"//submodules/Utils/DeviceModel",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -224,7 +224,7 @@ public func logoutOptionsController(context: AccountContext, navigationControlle
|
|||
|
||||
context.sharedContext.openResolvedUrl(resolvedUrl, context: context, urlContext: .generic, navigationController: navigationController, forceExternal: false, forceUpdate: false, openPeer: { peer, navigation in
|
||||
}, sendFile: nil, sendSticker: nil, sendEmoji: nil, requestMessageActionUrlAuth: nil, joinVoiceChat: nil, present: { controller, arguments in
|
||||
pushControllerImpl?(controller)
|
||||
presentControllerImpl?(controller, nil)
|
||||
}, dismissInput: {}, contentContext: nil, progress: nil, completion: nil)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -623,15 +623,12 @@ public func recentSessionsController(context: AccountContext, activeSessionsCont
|
|||
|
||||
let removeSessionImpl: (Int64, @escaping () -> Void) -> Void = { sessionId, completion in
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let controller = ActionSheetController(presentationData: presentationData)
|
||||
let dismissAction: () -> Void = { [weak controller] in
|
||||
controller?.dismissAnimated()
|
||||
}
|
||||
controller.setItemGroups([
|
||||
ActionSheetItemGroup(items: [
|
||||
ActionSheetTextItem(title: presentationData.strings.AuthSessions_TerminateSessionText),
|
||||
ActionSheetButtonItem(title: presentationData.strings.AuthSessions_TerminateSession, color: .destructive, action: {
|
||||
dismissAction()
|
||||
let controller = textAlertController(
|
||||
context: context,
|
||||
title: nil,
|
||||
text: presentationData.strings.AuthSessions_TerminateSessionText,
|
||||
actions: [
|
||||
TextAlertAction(type: .defaultDestructiveAction, title: presentationData.strings.AuthSessions_TerminateSession, action: {
|
||||
completion()
|
||||
|
||||
updateState {
|
||||
|
|
@ -649,11 +646,12 @@ public func recentSessionsController(context: AccountContext, activeSessionsCont
|
|||
}
|
||||
context.sharedContext.updateNotificationTokensRegistration()
|
||||
}))
|
||||
})
|
||||
]),
|
||||
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
||||
])
|
||||
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}),
|
||||
TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {})
|
||||
],
|
||||
actionLayout: .vertical
|
||||
)
|
||||
presentControllerImpl?(controller, nil)
|
||||
}
|
||||
|
||||
let removeWebSessionImpl: (Int64) -> Void = { sessionId in
|
||||
|
|
@ -690,16 +688,12 @@ public func recentSessionsController(context: AccountContext, activeSessionsCont
|
|||
removeSessionImpl(sessionId, {})
|
||||
}, terminateOtherSessions: {
|
||||
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||
let controller = ActionSheetController(presentationData: presentationData)
|
||||
let dismissAction: () -> Void = { [weak controller] in
|
||||
controller?.dismissAnimated()
|
||||
}
|
||||
controller.setItemGroups([
|
||||
ActionSheetItemGroup(items: [
|
||||
ActionSheetTextItem(title: presentationData.strings.AuthSessions_TerminateOtherSessionsText),
|
||||
ActionSheetButtonItem(title: presentationData.strings.AuthSessions_TerminateOtherSessions, color: .destructive, action: {
|
||||
dismissAction()
|
||||
|
||||
let controller = textAlertController(
|
||||
context: context,
|
||||
title: nil,
|
||||
text: presentationData.strings.AuthSessions_TerminateOtherSessionsText,
|
||||
actions: [
|
||||
TextAlertAction(type: .defaultDestructiveAction, title: presentationData.strings.AuthSessions_TerminateOtherSessions, action: {
|
||||
updateState {
|
||||
return $0.withUpdatedTerminatingOtherSessions(true)
|
||||
}
|
||||
|
|
@ -715,11 +709,12 @@ public func recentSessionsController(context: AccountContext, activeSessionsCont
|
|||
}
|
||||
context.sharedContext.updateNotificationTokensRegistration()
|
||||
}))
|
||||
})
|
||||
]),
|
||||
ActionSheetItemGroup(items: [ActionSheetButtonItem(title: presentationData.strings.Common_Cancel, action: { dismissAction() })])
|
||||
])
|
||||
presentControllerImpl?(controller, ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||
}),
|
||||
TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {})
|
||||
],
|
||||
actionLayout: .vertical
|
||||
)
|
||||
presentControllerImpl?(controller, nil)
|
||||
}, openSession: { session in
|
||||
let controller = RecentSessionScreen(context: context, subject: .session(session), updateAcceptSecretChats: { value in
|
||||
updateSessionDisposable.set(activeSessionsContext.updateSessionAcceptsSecretChats(session, accepts: value).start())
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -30,6 +30,7 @@ import ContextUI
|
|||
import QuickReactionSetupController
|
||||
import AvatarEditorScreen
|
||||
import PeerSelectionScreen
|
||||
import DeviceModel
|
||||
|
||||
enum SettingsSearchableItemIcon {
|
||||
case profile
|
||||
|
|
@ -3895,7 +3896,7 @@ private func appearanceSearchableItems(context: AccountContext) -> [SettingsSear
|
|||
present(.push, themeSettingsController(context: context, focusOnItemTag: itemTag))
|
||||
}
|
||||
|
||||
return [
|
||||
var items: [SettingsSearchableItem] = [
|
||||
SettingsSearchableItem(
|
||||
id: "appearance",
|
||||
title: strings.Settings_Appearance,
|
||||
|
|
@ -4094,14 +4095,31 @@ private func appearanceSearchableItems(context: AccountContext) -> [SettingsSear
|
|||
),
|
||||
SettingsSearchableItem(
|
||||
id: "appearance/tap-for-next-media",
|
||||
title: strings.Appearance_ShowNextMediaOnTap,
|
||||
icon: icon,
|
||||
breadcrumbs: [strings.Settings_Appearance],
|
||||
isVisible: false,
|
||||
present: { context, _, present in
|
||||
presentAppearanceSettings(context, present, .tapForNextMedia)
|
||||
}
|
||||
),
|
||||
)
|
||||
]
|
||||
|
||||
if DeviceModel.current.isIpad {
|
||||
items.append(
|
||||
SettingsSearchableItem(
|
||||
id: "appearance/send-with-cmd-enter",
|
||||
title: strings.Appearance_SendWithCmdEnter,
|
||||
icon: icon,
|
||||
breadcrumbs: [strings.Settings_Appearance],
|
||||
isVisible: false,
|
||||
present: { context, _, present in
|
||||
presentAppearanceSettings(context, present, .sendWithCmdEnter)
|
||||
}
|
||||
)
|
||||
)
|
||||
}
|
||||
return items
|
||||
}
|
||||
|
||||
private func languageSearchableItems(context: AccountContext, localizations: [LocalizationInfo]) -> [SettingsSearchableItem] {
|
||||
|
|
|
|||
|
|
@ -12,12 +12,7 @@ import AppBundle
|
|||
private func generateBorderImage(theme: PresentationTheme, bordered: Bool, selected: Bool) -> UIImage? {
|
||||
return generateImage(CGSize(width: 30.0, height: 30.0), rotatedContext: { size, context in
|
||||
let bounds = CGRect(origin: CGPoint(), size: size)
|
||||
context.setFillColor(theme.list.itemBlocksBackgroundColor.cgColor)
|
||||
context.fill(bounds)
|
||||
|
||||
context.setBlendMode(.clear)
|
||||
context.fillEllipse(in: bounds)
|
||||
context.setBlendMode(.normal)
|
||||
context.clear(bounds)
|
||||
|
||||
let lineWidth: CGFloat
|
||||
if selected {
|
||||
|
|
@ -113,8 +108,10 @@ private final class ThemeSettingsAppIconNode : ASDisplayNode {
|
|||
|
||||
override init() {
|
||||
self.iconNode = ASImageNode()
|
||||
self.iconNode.clipsToBounds = true
|
||||
self.iconNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 63.0, height: 63.0))
|
||||
self.iconNode.isLayerBacked = true
|
||||
self.iconNode.cornerRadius = 15.0
|
||||
|
||||
self.overlayNode = ASImageNode()
|
||||
self.overlayNode.frame = CGRect(origin: CGPoint(), size: CGSize(width: 63.0, height: 63.0))
|
||||
|
|
@ -199,6 +196,7 @@ private let selectedTextFont = Font.medium(12.0)
|
|||
|
||||
class ThemeSettingsAppIconItemNode: ListViewItemNode, ItemListItemNode {
|
||||
private let backgroundNode: ASDisplayNode
|
||||
private let highlightNode: ASDisplayNode
|
||||
private let topStripeNode: ASDisplayNode
|
||||
private let bottomStripeNode: ASDisplayNode
|
||||
private let maskNode: ASImageNode
|
||||
|
|
@ -219,6 +217,9 @@ class ThemeSettingsAppIconItemNode: ListViewItemNode, ItemListItemNode {
|
|||
self.backgroundNode = ASDisplayNode()
|
||||
self.backgroundNode.isLayerBacked = true
|
||||
|
||||
self.highlightNode = ASDisplayNode()
|
||||
self.highlightNode.isLayerBacked = true
|
||||
|
||||
self.topStripeNode = ASDisplayNode()
|
||||
self.topStripeNode.isLayerBacked = true
|
||||
|
||||
|
|
@ -234,6 +235,20 @@ class ThemeSettingsAppIconItemNode: ListViewItemNode, ItemListItemNode {
|
|||
self.addSubnode(self.containerNode)
|
||||
}
|
||||
|
||||
public func displayHighlight() {
|
||||
if self.backgroundNode.supernode != nil {
|
||||
self.insertSubnode(self.highlightNode, aboveSubnode: self.backgroundNode)
|
||||
} else {
|
||||
self.insertSubnode(self.highlightNode, at: 0)
|
||||
}
|
||||
|
||||
Queue.mainQueue().after(1.2, {
|
||||
self.highlightNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { _ in
|
||||
self.highlightNode.removeFromSupernode()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func asyncLayout() -> (_ item: ThemeSettingsAppIconItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
|
||||
return { item, params, neighbors in
|
||||
let contentSize: CGSize
|
||||
|
|
@ -280,6 +295,7 @@ class ThemeSettingsAppIconItemNode: ListViewItemNode, ItemListItemNode {
|
|||
strongSelf.backgroundNode.backgroundColor = item.theme.list.itemBlocksBackgroundColor
|
||||
strongSelf.topStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor
|
||||
strongSelf.bottomStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor
|
||||
strongSelf.highlightNode.backgroundColor = item.theme.list.itemSearchHighlightColor
|
||||
|
||||
if strongSelf.backgroundNode.supernode == nil {
|
||||
strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0)
|
||||
|
|
@ -321,6 +337,7 @@ class ThemeSettingsAppIconItemNode: ListViewItemNode, ItemListItemNode {
|
|||
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners, glass: item.systemStyle == .glass) : nil
|
||||
|
||||
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
|
||||
strongSelf.highlightNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
|
||||
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
|
||||
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight))
|
||||
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset, height: separatorHeight))
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ import ThemeCarouselItem
|
|||
import ThemeAccentColorScreen
|
||||
import WallpaperGridScreen
|
||||
import PeerNameColorItem
|
||||
import DeviceModel
|
||||
|
||||
private final class ThemeSettingsControllerArguments {
|
||||
let context: AccountContext
|
||||
|
|
@ -38,13 +39,34 @@ private final class ThemeSettingsControllerArguments {
|
|||
let openBubbleSettings: () -> Void
|
||||
let openPowerSavingSettings: () -> Void
|
||||
let openStickersAndEmoji: () -> Void
|
||||
let toggleSendWithCmdEnter: (Bool) -> Void
|
||||
let toggleShowNextMediaOnTap: (Bool) -> Void
|
||||
let selectAppIcon: (PresentationAppIcon) -> Void
|
||||
let editTheme: (PresentationCloudTheme) -> Void
|
||||
let themeContextAction: (Bool, PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void
|
||||
let colorContextAction: (Bool, PresentationThemeReference, ThemeSettingsColorOption?, ASDisplayNode, ContextGesture?) -> Void
|
||||
|
||||
init(context: AccountContext, selectTheme: @escaping (PresentationThemeReference) -> Void, openThemeSettings: @escaping () -> Void, openWallpaperSettings: @escaping () -> Void, openNameColorSettings: @escaping () -> Void, selectAccentColor: @escaping (PresentationThemeAccentColor?) -> Void, openAccentColorPicker: @escaping (PresentationThemeReference, Bool) -> Void, toggleNightTheme: @escaping (Bool) -> Void, openAutoNightTheme: @escaping () -> Void, openTextSize: @escaping () -> Void, openBubbleSettings: @escaping () -> Void, openPowerSavingSettings: @escaping () -> Void, openStickersAndEmoji: @escaping () -> Void, toggleShowNextMediaOnTap: @escaping (Bool) -> Void, selectAppIcon: @escaping (PresentationAppIcon) -> Void, editTheme: @escaping (PresentationCloudTheme) -> Void, themeContextAction: @escaping (Bool, PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void, colorContextAction: @escaping (Bool, PresentationThemeReference, ThemeSettingsColorOption?, ASDisplayNode, ContextGesture?) -> Void) {
|
||||
init(
|
||||
context: AccountContext,
|
||||
selectTheme: @escaping (PresentationThemeReference) -> Void,
|
||||
openThemeSettings: @escaping () -> Void,
|
||||
openWallpaperSettings: @escaping () -> Void,
|
||||
openNameColorSettings: @escaping () -> Void,
|
||||
selectAccentColor: @escaping (PresentationThemeAccentColor?) -> Void,
|
||||
openAccentColorPicker: @escaping (PresentationThemeReference, Bool) -> Void,
|
||||
toggleNightTheme: @escaping (Bool) -> Void,
|
||||
openAutoNightTheme: @escaping () -> Void,
|
||||
openTextSize: @escaping () -> Void,
|
||||
openBubbleSettings: @escaping () -> Void,
|
||||
openPowerSavingSettings: @escaping () -> Void,
|
||||
openStickersAndEmoji: @escaping () -> Void,
|
||||
toggleSendWithCmdEnter: @escaping (Bool) -> Void,
|
||||
toggleShowNextMediaOnTap: @escaping (Bool) -> Void,
|
||||
selectAppIcon: @escaping (PresentationAppIcon) -> Void,
|
||||
editTheme: @escaping (PresentationCloudTheme) -> Void,
|
||||
themeContextAction: @escaping (Bool, PresentationThemeReference, ASDisplayNode, ContextGesture?) -> Void,
|
||||
colorContextAction: @escaping (Bool, PresentationThemeReference, ThemeSettingsColorOption?, ASDisplayNode, ContextGesture?) -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.selectTheme = selectTheme
|
||||
self.openThemeSettings = openThemeSettings
|
||||
|
|
@ -58,6 +80,7 @@ private final class ThemeSettingsControllerArguments {
|
|||
self.openBubbleSettings = openBubbleSettings
|
||||
self.openPowerSavingSettings = openPowerSavingSettings
|
||||
self.openStickersAndEmoji = openStickersAndEmoji
|
||||
self.toggleSendWithCmdEnter = toggleSendWithCmdEnter
|
||||
self.toggleShowNextMediaOnTap = toggleShowNextMediaOnTap
|
||||
self.selectAppIcon = selectAppIcon
|
||||
self.editTheme = editTheme
|
||||
|
|
@ -84,6 +107,7 @@ public enum ThemeSettingsEntryTag: ItemListItemTag {
|
|||
case powerSaving
|
||||
case stickersAndEmoji
|
||||
case animations
|
||||
case sendWithCmdEnter
|
||||
case tapForNextMedia
|
||||
case nightMode
|
||||
|
||||
|
|
@ -112,6 +136,7 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
|
|||
case powerSaving
|
||||
case stickersAndEmoji
|
||||
case otherHeader(PresentationTheme, String)
|
||||
case sendWithCmdEnter(PresentationTheme, String, Bool)
|
||||
case showNextMediaOnTap(PresentationTheme, String, Bool)
|
||||
case showNextMediaOnTapInfo(PresentationTheme, String)
|
||||
|
||||
|
|
@ -127,7 +152,7 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
|
|||
return ThemeSettingsControllerSection.icon.rawValue
|
||||
case .powerSaving, .stickersAndEmoji:
|
||||
return ThemeSettingsControllerSection.message.rawValue
|
||||
case .otherHeader, .showNextMediaOnTap, .showNextMediaOnTapInfo:
|
||||
case .otherHeader, .sendWithCmdEnter, .showNextMediaOnTap, .showNextMediaOnTapInfo:
|
||||
return ThemeSettingsControllerSection.other.rawValue
|
||||
}
|
||||
}
|
||||
|
|
@ -164,10 +189,12 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
|
|||
return 13
|
||||
case .otherHeader:
|
||||
return 14
|
||||
case .showNextMediaOnTap:
|
||||
case .sendWithCmdEnter:
|
||||
return 15
|
||||
case .showNextMediaOnTapInfo:
|
||||
case .showNextMediaOnTap:
|
||||
return 16
|
||||
case .showNextMediaOnTapInfo:
|
||||
return 17
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -263,6 +290,12 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
|
|||
} else {
|
||||
return false
|
||||
}
|
||||
case let .sendWithCmdEnter(lhsTheme, lhsTitle, lhsValue):
|
||||
if case let .sendWithCmdEnter(rhsTheme, rhsTitle, rhsValue) = rhs, lhsTheme === rhsTheme, lhsTitle == rhsTitle, lhsValue == rhsValue {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
case let .showNextMediaOnTap(lhsTheme, lhsTitle, lhsValue):
|
||||
if case let .showNextMediaOnTap(rhsTheme, rhsTitle, rhsValue) = rhs, lhsTheme === rhsTheme, lhsTitle == rhsTitle, lhsValue == rhsValue {
|
||||
return true
|
||||
|
|
@ -351,6 +384,10 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
|
|||
})
|
||||
case let .otherHeader(_, text):
|
||||
return ItemListSectionHeaderItem(presentationData: presentationData, text: text, sectionId: self.section)
|
||||
case let .sendWithCmdEnter(_, title, value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, systemStyle: .glass, title: title, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
arguments.toggleSendWithCmdEnter(value)
|
||||
}, tag: ThemeSettingsEntryTag.sendWithCmdEnter)
|
||||
case let .showNextMediaOnTap(_, title, value):
|
||||
return ItemListSwitchItem(presentationData: presentationData, systemStyle: .glass, title: title, value: value, sectionId: self.section, style: .blocks, updated: { value in
|
||||
arguments.toggleShowNextMediaOnTap(value)
|
||||
|
|
@ -361,7 +398,21 @@ private enum ThemeSettingsControllerEntry: ItemListNodeEntry {
|
|||
}
|
||||
}
|
||||
|
||||
private func themeSettingsControllerEntries(presentationData: PresentationData, presentationThemeSettings: PresentationThemeSettings, mediaSettings: MediaDisplaySettings, themeReference: PresentationThemeReference, availableThemes: [PresentationThemeReference], availableAppIcons: [PresentationAppIcon], currentAppIconName: String?, isPremium: Bool, chatThemes: [PresentationThemeReference], animatedEmojiStickers: [String: [StickerPackItem]], accountPeer: EnginePeer?, nameColors: PeerNameColors) -> [ThemeSettingsControllerEntry] {
|
||||
private func themeSettingsControllerEntries(
|
||||
presentationData: PresentationData,
|
||||
presentationThemeSettings: PresentationThemeSettings,
|
||||
chatSettings: ChatSettings,
|
||||
mediaSettings: MediaDisplaySettings,
|
||||
themeReference: PresentationThemeReference,
|
||||
availableThemes: [PresentationThemeReference],
|
||||
availableAppIcons: [PresentationAppIcon],
|
||||
currentAppIconName: String?,
|
||||
isPremium: Bool,
|
||||
chatThemes: [PresentationThemeReference],
|
||||
animatedEmojiStickers: [String: [StickerPackItem]],
|
||||
accountPeer: EnginePeer?,
|
||||
nameColors: PeerNameColors
|
||||
) -> [ThemeSettingsControllerEntry] {
|
||||
var entries: [ThemeSettingsControllerEntry] = []
|
||||
|
||||
let strings = presentationData.strings
|
||||
|
|
@ -437,6 +488,9 @@ private func themeSettingsControllerEntries(presentationData: PresentationData,
|
|||
}
|
||||
|
||||
entries.append(.otherHeader(presentationData.theme, strings.Appearance_Other.uppercased()))
|
||||
if DeviceModel.current.isIpad {
|
||||
entries.append(.sendWithCmdEnter(presentationData.theme, strings.Appearance_SendWithCmdEnter, chatSettings.sendWithCmdEnter))
|
||||
}
|
||||
entries.append(.showNextMediaOnTap(presentationData.theme, strings.Appearance_ShowNextMediaOnTap, mediaSettings.showNextMediaOnTap))
|
||||
entries.append(.showNextMediaOnTapInfo(presentationData.theme, strings.Appearance_ShowNextMediaOnTapInfo))
|
||||
|
||||
|
|
@ -557,6 +611,10 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
|||
pushControllerImpl?(installedStickerPacksController(context: context, mode: .general, archivedPacks: archivedStickerPacks, updatedPacks: { _ in
|
||||
}))
|
||||
})
|
||||
}, toggleSendWithCmdEnter: { value in
|
||||
let _ = updateChatSettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
|
||||
return current.withUpdatedSendWithCmdEnter(value)
|
||||
}).start()
|
||||
}, toggleShowNextMediaOnTap: { value in
|
||||
let _ = updateMediaDisplaySettingsInteractively(accountManager: context.sharedContext.accountManager, { current in
|
||||
return current.withUpdatedShowNextMediaOnTap(value)
|
||||
|
|
@ -1035,9 +1093,26 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
|||
})
|
||||
})
|
||||
|
||||
let signal = combineLatest(queue: .mainQueue(), context.sharedContext.presentationData, context.sharedContext.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.presentationThemeSettings, SharedDataKeys.chatThemes, ApplicationSpecificSharedDataKeys.mediaDisplaySettings]), cloudThemes.get(), availableAppIcons, currentAppIconName.get(), removedThemeIndexesPromise.get(), animatedEmojiStickers, context.account.postbox.peerView(id: context.account.peerId), context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)))
|
||||
let signal = combineLatest(
|
||||
queue: .mainQueue(),
|
||||
context.sharedContext.presentationData,
|
||||
context.sharedContext.accountManager.sharedData(keys: [
|
||||
ApplicationSpecificSharedDataKeys.presentationThemeSettings,
|
||||
ApplicationSpecificSharedDataKeys.chatSettings,
|
||||
ApplicationSpecificSharedDataKeys.mediaDisplaySettings,
|
||||
SharedDataKeys.chatThemes
|
||||
]),
|
||||
cloudThemes.get(),
|
||||
availableAppIcons,
|
||||
currentAppIconName.get(),
|
||||
removedThemeIndexesPromise.get(),
|
||||
animatedEmojiStickers,
|
||||
context.account.postbox.peerView(id: context.account.peerId),
|
||||
context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId))
|
||||
)
|
||||
|> map { presentationData, sharedData, cloudThemes, availableAppIcons, currentAppIconName, removedThemeIndexes, animatedEmojiStickers, peerView, accountPeer -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||
let settings = sharedData.entries[ApplicationSpecificSharedDataKeys.presentationThemeSettings]?.get(PresentationThemeSettings.self) ?? PresentationThemeSettings.defaultSettings
|
||||
let chatSettings = sharedData.entries[ApplicationSpecificSharedDataKeys.chatSettings]?.get(ChatSettings.self) ?? ChatSettings.defaultSettings
|
||||
let mediaSettings = sharedData.entries[ApplicationSpecificSharedDataKeys.mediaDisplaySettings]?.get(MediaDisplaySettings.self) ?? MediaDisplaySettings.defaultSettings
|
||||
|
||||
let isPremium = peerView.peers[peerView.peerId]?.isPremium ?? false
|
||||
|
|
@ -1077,7 +1152,7 @@ public func themeSettingsController(context: AccountContext, focusOnItemTag: The
|
|||
chatThemes.insert(.builtin(.dayClassic), at: 0)
|
||||
|
||||
let controllerState = ItemListControllerState(presentationData: ItemListPresentationData(presentationData), title: .text(presentationData.strings.Appearance_Title), leftNavigationButton: nil, rightNavigationButton: nil, backNavigationButton: ItemListBackButton(title: presentationData.strings.Common_Back))
|
||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: themeSettingsControllerEntries(presentationData: presentationData, presentationThemeSettings: settings, mediaSettings: mediaSettings, themeReference: themeReference, availableThemes: availableThemes, availableAppIcons: availableAppIcons, currentAppIconName: currentAppIconName, isPremium: isPremium, chatThemes: chatThemes, animatedEmojiStickers: animatedEmojiStickers, accountPeer: accountPeer, nameColors: context.peerNameColors), style: .blocks, ensureVisibleItemTag: focusOnItemTag, animateChanges: false)
|
||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: themeSettingsControllerEntries(presentationData: presentationData, presentationThemeSettings: settings, chatSettings: chatSettings, mediaSettings: mediaSettings, themeReference: themeReference, availableThemes: availableThemes, availableAppIcons: availableAppIcons, currentAppIconName: currentAppIconName, isPremium: isPremium, chatThemes: chatThemes, animatedEmojiStickers: animatedEmojiStickers, accountPeer: accountPeer, nameColors: context.peerNameColors), style: .blocks, ensureVisibleItemTag: focusOnItemTag, animateChanges: false)
|
||||
|
||||
return (controllerState, (listState, arguments))
|
||||
}
|
||||
|
|
|
|||
|
|
@ -289,9 +289,9 @@ private final class StickerPackContainer: ASDisplayNode {
|
|||
self.addSubnode(self.gridNode)
|
||||
|
||||
self.titleContainer.addSubnode(self.titleNode)
|
||||
self.addSubnode(self.topContainerNode)
|
||||
self.addSubnode(self.titleContainer)
|
||||
|
||||
self.addSubnode(self.topContainerNode)
|
||||
self.addSubnode(self.bottomContainerNode)
|
||||
|
||||
self.gridNode.presentationLayoutUpdated = { [weak self] presentationLayout, transition in
|
||||
|
|
@ -654,6 +654,13 @@ private final class StickerPackContainer: ASDisplayNode {
|
|||
self.reorderingGestureRecognizer = reorderingGestureRecognizer
|
||||
self.gridNode.view.addGestureRecognizer(reorderingGestureRecognizer)
|
||||
|
||||
self.gridNode.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
||||
self.gridNode.clipsToBounds = true
|
||||
self.gridNode.layer.cornerRadius = 40.0
|
||||
|
||||
self.topEdgeEffectView.clipsToBounds = true
|
||||
self.topEdgeEffectView.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
||||
self.topEdgeEffectView.layer.cornerRadius = 40.0
|
||||
self.topContainerNode.view.addSubview(self.topEdgeEffectView)
|
||||
self.bottomContainerNode.view.addSubview(self.bottomEdgeEffectView)
|
||||
}
|
||||
|
|
@ -1891,7 +1898,7 @@ private final class StickerPackContainer: ASDisplayNode {
|
|||
|
||||
transition.updateFrame(node: self.bottomContainerNode, frame: CGRect(origin: CGPoint(x: 0.0, y: layout.size.height - actionAreaHeight), size: CGSize(width: layout.size.width, height: 90.0)))
|
||||
|
||||
let gridFrame = CGRect(origin: CGPoint(x: 0.0, y: insets.top + titleAreaInset), size: CGSize(width: layout.size.width, height: layout.size.height - insets.top - titleAreaInset))
|
||||
let gridFrame = CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: layout.size.height - insets.top))
|
||||
|
||||
let itemsPerRow = 5
|
||||
let fillingWidth = horizontalContainerFillingSizeForLayout(layout: layout, sideInset: 0.0)
|
||||
|
|
@ -1923,12 +1930,13 @@ private final class StickerPackContainer: ASDisplayNode {
|
|||
|
||||
let initialRevealedRowCount: CGFloat = 4.5
|
||||
|
||||
let topInset: CGFloat
|
||||
var topInset: CGFloat
|
||||
if case .regular = layout.metrics.widthClass {
|
||||
topInset = 0.0
|
||||
topInset = titleAreaInset
|
||||
} else {
|
||||
topInset = insets.top + max(0.0, layout.size.height - floor(initialRevealedRowCount * itemWidth) - insets.top - actionAreaHeight - titleAreaInset)
|
||||
}
|
||||
|
||||
let additionalGridBottomInset = max(0.0, gridFrame.size.height - actionAreaHeight - contentHeight)
|
||||
let gridInsets = UIEdgeInsets(top: topInset, left: gridLeftInset, bottom: actionAreaHeight + additionalGridBottomInset, right: layout.size.width - fillingWidth - gridLeftInset)
|
||||
|
||||
|
|
@ -2112,6 +2120,10 @@ private final class StickerPackContainer: ASDisplayNode {
|
|||
buttonView.frame = buttonFrame
|
||||
}
|
||||
|
||||
let topEdgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: layout.size.width, height: 90.0))
|
||||
transition.updateFrame(view: self.topEdgeEffectView, frame: topEdgeEffectFrame)
|
||||
self.topEdgeEffectView.update(content: self.presentationData.theme.actionSheet.opaqueItemBackgroundColor, blur: true, alpha: 0.65, rect: topEdgeEffectFrame, edge: .top, edgeSize: topEdgeEffectFrame.height, transition: ComponentTransition(transition))
|
||||
|
||||
let bottomEdgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: actionAreaHeight - 90.0), size: CGSize(width: layout.size.width, height: 90.0))
|
||||
transition.updateFrame(view: self.bottomEdgeEffectView, frame: bottomEdgeEffectFrame)
|
||||
self.bottomEdgeEffectView.update(content: self.presentationData.theme.actionSheet.opaqueItemBackgroundColor, blur: true, alpha: 0.65, rect: bottomEdgeEffectFrame, edge: .bottom, edgeSize: bottomEdgeEffectFrame.height, transition: ComponentTransition(transition))
|
||||
|
|
@ -2133,7 +2145,7 @@ private final class StickerPackContainer: ASDisplayNode {
|
|||
return
|
||||
}
|
||||
|
||||
let minBackgroundY = gridFrame.minY - titleAreaInset
|
||||
let minBackgroundY = gridFrame.minY
|
||||
let unclippedBackgroundY = gridFrame.minY - presentationLayout.contentOffset.y - titleAreaInset
|
||||
|
||||
let offsetFromInitialPosition = presentationLayout.contentOffset.y + gridInsets.top
|
||||
|
|
@ -2173,7 +2185,7 @@ private final class StickerPackContainer: ASDisplayNode {
|
|||
var titleContainerFrame: CGRect
|
||||
if case .regular = layout.metrics.widthClass {
|
||||
backgroundFrame.origin.y = min(0.0, backgroundFrame.origin.y)
|
||||
titleContainerFrame = CGRect(origin: CGPoint(x: backgroundFrame.minX + floor((backgroundFrame.width) / 2.0), y: floor((56.0) / 2.0)), size: CGSize())
|
||||
titleContainerFrame = CGRect(origin: CGPoint(x: backgroundFrame.minX + floor((backgroundFrame.width) / 2.0), y: floor((56.0) / 2.0) + 10.0), size: CGSize())
|
||||
} else {
|
||||
titleContainerFrame = CGRect(origin: CGPoint(x: backgroundFrame.minX + floor((backgroundFrame.width) / 2.0), y: backgroundFrame.minY + floor((56.0) / 2.0) + 10.0), size: CGSize())
|
||||
}
|
||||
|
|
@ -3103,12 +3115,12 @@ private final class StickerPackContextReferenceContentSource: ContextReferenceCo
|
|||
|
||||
|
||||
private func generateShadowImage() -> UIImage? {
|
||||
return generateImage(CGSize(width: 140.0, height: 140.0), rotatedContext: { size, context in
|
||||
return generateImage(CGSize(width: 220.0, height: 220.0), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
context.saveGState()
|
||||
context.setShadow(offset: CGSize(), blur: 60.0, color: UIColor(white: 0.0, alpha: 0.4).cgColor)
|
||||
let path = UIBezierPath(roundedRect: CGRect(x: 60.0, y: 60.0, width: 20.0, height: 20.0), cornerRadius: 10.0).cgPath
|
||||
let path = UIBezierPath(roundedRect: CGRect(x: 60.0, y: 60.0, width: 100.0, height: 100.0), cornerRadius: 40.0).cgPath
|
||||
context.addPath(path)
|
||||
context.fillPath()
|
||||
|
||||
|
|
@ -3117,7 +3129,7 @@ private func generateShadowImage() -> UIImage? {
|
|||
context.setBlendMode(.clear)
|
||||
context.addPath(path)
|
||||
context.fillPath()
|
||||
})?.stretchableImage(withLeftCapWidth: 70, topCapHeight: 70)
|
||||
})?.stretchableImage(withLeftCapWidth: 110, topCapHeight: 110)
|
||||
}
|
||||
|
||||
private func generateArrowImage(color: UIColor) -> UIImage? {
|
||||
|
|
@ -3290,38 +3302,6 @@ private class ReorderingGestureRecognizer: UIGestureRecognizer {
|
|||
}
|
||||
}
|
||||
|
||||
private func generateShadowImage(corners: CACornerMask, radius: CGFloat) -> UIImage? {
|
||||
return generateImage(CGSize(width: 120.0, height: 120), rotatedContext: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
// context.saveGState()
|
||||
context.setShadow(offset: CGSize(), blur: 28.0, color: UIColor(white: 0.0, alpha: 0.4).cgColor)
|
||||
|
||||
var rectCorners: UIRectCorner = []
|
||||
if corners.contains(.layerMinXMinYCorner) {
|
||||
rectCorners.insert(.topLeft)
|
||||
}
|
||||
if corners.contains(.layerMaxXMinYCorner) {
|
||||
rectCorners.insert(.topRight)
|
||||
}
|
||||
if corners.contains(.layerMinXMaxYCorner) {
|
||||
rectCorners.insert(.bottomLeft)
|
||||
}
|
||||
if corners.contains(.layerMaxXMaxYCorner) {
|
||||
rectCorners.insert(.bottomRight)
|
||||
}
|
||||
|
||||
let path = UIBezierPath(roundedRect: CGRect(x: 30.0, y: 30.0, width: 60.0, height: 60.0), byRoundingCorners: rectCorners, cornerRadii: CGSize(width: radius, height: radius)).cgPath
|
||||
context.addPath(path)
|
||||
context.fillPath()
|
||||
// context.restoreGState()
|
||||
|
||||
// context.setBlendMode(.clear)
|
||||
// context.addPath(path)
|
||||
// context.fillPath()
|
||||
})?.stretchableImage(withLeftCapWidth: 60, topCapHeight: 60)
|
||||
}
|
||||
|
||||
private final class CopyView: UIView {
|
||||
let shadow: UIImageView
|
||||
var snapshotView: UIView?
|
||||
|
|
|
|||
|
|
@ -997,6 +997,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||
dict[2029777832] = { return Api.StarGiftAttributeRarity.parse_starGiftAttributeRarityEpic($0) }
|
||||
dict[-822614104] = { return Api.StarGiftAttributeRarity.parse_starGiftAttributeRarityLegendary($0) }
|
||||
dict[-259174037] = { return Api.StarGiftAttributeRarity.parse_starGiftAttributeRarityRare($0) }
|
||||
dict[-607231095] = { return Api.StarGiftAttributeRarity.parse_starGiftAttributeRarityUncommon($0) }
|
||||
dict[1118831432] = { return Api.StarGiftAuctionAcquiredGift.parse_starGiftAuctionAcquiredGift($0) }
|
||||
dict[984483112] = { return Api.StarGiftAuctionRound.parse_starGiftAuctionRound($0) }
|
||||
dict[178266597] = { return Api.StarGiftAuctionRound.parse_starGiftAuctionRoundExtendable($0) }
|
||||
|
|
|
|||
|
|
@ -1318,6 +1318,7 @@ public extension Api {
|
|||
case starGiftAttributeRarityEpic
|
||||
case starGiftAttributeRarityLegendary
|
||||
case starGiftAttributeRarityRare
|
||||
case starGiftAttributeRarityUncommon
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
|
|
@ -1342,6 +1343,11 @@ public extension Api {
|
|||
buffer.appendInt32(-259174037)
|
||||
}
|
||||
break
|
||||
case .starGiftAttributeRarityUncommon:
|
||||
if boxed {
|
||||
buffer.appendInt32(-607231095)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1355,6 +1361,8 @@ public extension Api {
|
|||
return ("starGiftAttributeRarityLegendary", [])
|
||||
case .starGiftAttributeRarityRare:
|
||||
return ("starGiftAttributeRarityRare", [])
|
||||
case .starGiftAttributeRarityUncommon:
|
||||
return ("starGiftAttributeRarityUncommon", [])
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1378,6 +1386,9 @@ public extension Api {
|
|||
public static func parse_starGiftAttributeRarityRare(_ reader: BufferReader) -> StarGiftAttributeRarity? {
|
||||
return Api.StarGiftAttributeRarity.starGiftAttributeRarityRare
|
||||
}
|
||||
public static func parse_starGiftAttributeRarityUncommon(_ reader: BufferReader) -> StarGiftAttributeRarity? {
|
||||
return Api.StarGiftAttributeRarity.starGiftAttributeRarityUncommon
|
||||
}
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
|
|
@ -2127,76 +2138,3 @@ public extension Api {
|
|||
}
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum StarsAmount: TypeConstructorDescription {
|
||||
public class Cons_starsAmount {
|
||||
public var amount: Int64
|
||||
public var nanos: Int32
|
||||
public init(amount: Int64, nanos: Int32) {
|
||||
self.amount = amount
|
||||
self.nanos = nanos
|
||||
}
|
||||
}
|
||||
public class Cons_starsTonAmount {
|
||||
public var amount: Int64
|
||||
public init(amount: Int64) {
|
||||
self.amount = amount
|
||||
}
|
||||
}
|
||||
case starsAmount(Cons_starsAmount)
|
||||
case starsTonAmount(Cons_starsTonAmount)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .starsAmount(let _data):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1145654109)
|
||||
}
|
||||
serializeInt64(_data.amount, buffer: buffer, boxed: false)
|
||||
serializeInt32(_data.nanos, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .starsTonAmount(let _data):
|
||||
if boxed {
|
||||
buffer.appendInt32(1957618656)
|
||||
}
|
||||
serializeInt64(_data.amount, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .starsAmount(let _data):
|
||||
return ("starsAmount", [("amount", _data.amount as Any), ("nanos", _data.nanos as Any)])
|
||||
case .starsTonAmount(let _data):
|
||||
return ("starsTonAmount", [("amount", _data.amount as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_starsAmount(_ reader: BufferReader) -> StarsAmount? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.StarsAmount.starsAmount(Cons_starsAmount(amount: _1!, nanos: _2!))
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_starsTonAmount(_ reader: BufferReader) -> StarsAmount? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.StarsAmount.starsTonAmount(Cons_starsTonAmount(amount: _1!))
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,76 @@
|
|||
public extension Api {
|
||||
enum StarsAmount: TypeConstructorDescription {
|
||||
public class Cons_starsAmount {
|
||||
public var amount: Int64
|
||||
public var nanos: Int32
|
||||
public init(amount: Int64, nanos: Int32) {
|
||||
self.amount = amount
|
||||
self.nanos = nanos
|
||||
}
|
||||
}
|
||||
public class Cons_starsTonAmount {
|
||||
public var amount: Int64
|
||||
public init(amount: Int64) {
|
||||
self.amount = amount
|
||||
}
|
||||
}
|
||||
case starsAmount(Cons_starsAmount)
|
||||
case starsTonAmount(Cons_starsTonAmount)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .starsAmount(let _data):
|
||||
if boxed {
|
||||
buffer.appendInt32(-1145654109)
|
||||
}
|
||||
serializeInt64(_data.amount, buffer: buffer, boxed: false)
|
||||
serializeInt32(_data.nanos, buffer: buffer, boxed: false)
|
||||
break
|
||||
case .starsTonAmount(let _data):
|
||||
if boxed {
|
||||
buffer.appendInt32(1957618656)
|
||||
}
|
||||
serializeInt64(_data.amount, buffer: buffer, boxed: false)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .starsAmount(let _data):
|
||||
return ("starsAmount", [("amount", _data.amount as Any), ("nanos", _data.nanos as Any)])
|
||||
case .starsTonAmount(let _data):
|
||||
return ("starsTonAmount", [("amount", _data.amount as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_starsAmount(_ reader: BufferReader) -> StarsAmount? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.StarsAmount.starsAmount(Cons_starsAmount(amount: _1!, nanos: _2!))
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_starsTonAmount(_ reader: BufferReader) -> StarsAmount? {
|
||||
var _1: Int64?
|
||||
_1 = reader.readInt64()
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.StarsAmount.starsTonAmount(Cons_starsTonAmount(amount: _1!))
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum StarsGiftOption: TypeConstructorDescription {
|
||||
public class Cons_starsGiftOption {
|
||||
|
|
@ -1662,186 +1735,3 @@ public extension Api {
|
|||
}
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum StickerSetCovered: TypeConstructorDescription {
|
||||
public class Cons_stickerSetCovered {
|
||||
public var set: Api.StickerSet
|
||||
public var cover: Api.Document
|
||||
public init(set: Api.StickerSet, cover: Api.Document) {
|
||||
self.set = set
|
||||
self.cover = cover
|
||||
}
|
||||
}
|
||||
public class Cons_stickerSetFullCovered {
|
||||
public var set: Api.StickerSet
|
||||
public var packs: [Api.StickerPack]
|
||||
public var keywords: [Api.StickerKeyword]
|
||||
public var documents: [Api.Document]
|
||||
public init(set: Api.StickerSet, packs: [Api.StickerPack], keywords: [Api.StickerKeyword], documents: [Api.Document]) {
|
||||
self.set = set
|
||||
self.packs = packs
|
||||
self.keywords = keywords
|
||||
self.documents = documents
|
||||
}
|
||||
}
|
||||
public class Cons_stickerSetMultiCovered {
|
||||
public var set: Api.StickerSet
|
||||
public var covers: [Api.Document]
|
||||
public init(set: Api.StickerSet, covers: [Api.Document]) {
|
||||
self.set = set
|
||||
self.covers = covers
|
||||
}
|
||||
}
|
||||
public class Cons_stickerSetNoCovered {
|
||||
public var set: Api.StickerSet
|
||||
public init(set: Api.StickerSet) {
|
||||
self.set = set
|
||||
}
|
||||
}
|
||||
case stickerSetCovered(Cons_stickerSetCovered)
|
||||
case stickerSetFullCovered(Cons_stickerSetFullCovered)
|
||||
case stickerSetMultiCovered(Cons_stickerSetMultiCovered)
|
||||
case stickerSetNoCovered(Cons_stickerSetNoCovered)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .stickerSetCovered(let _data):
|
||||
if boxed {
|
||||
buffer.appendInt32(1678812626)
|
||||
}
|
||||
_data.set.serialize(buffer, true)
|
||||
_data.cover.serialize(buffer, true)
|
||||
break
|
||||
case .stickerSetFullCovered(let _data):
|
||||
if boxed {
|
||||
buffer.appendInt32(1087454222)
|
||||
}
|
||||
_data.set.serialize(buffer, true)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(_data.packs.count))
|
||||
for item in _data.packs {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(_data.keywords.count))
|
||||
for item in _data.keywords {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(_data.documents.count))
|
||||
for item in _data.documents {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
case .stickerSetMultiCovered(let _data):
|
||||
if boxed {
|
||||
buffer.appendInt32(872932635)
|
||||
}
|
||||
_data.set.serialize(buffer, true)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(_data.covers.count))
|
||||
for item in _data.covers {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
case .stickerSetNoCovered(let _data):
|
||||
if boxed {
|
||||
buffer.appendInt32(2008112412)
|
||||
}
|
||||
_data.set.serialize(buffer, true)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .stickerSetCovered(let _data):
|
||||
return ("stickerSetCovered", [("set", _data.set as Any), ("cover", _data.cover as Any)])
|
||||
case .stickerSetFullCovered(let _data):
|
||||
return ("stickerSetFullCovered", [("set", _data.set as Any), ("packs", _data.packs as Any), ("keywords", _data.keywords as Any), ("documents", _data.documents as Any)])
|
||||
case .stickerSetMultiCovered(let _data):
|
||||
return ("stickerSetMultiCovered", [("set", _data.set as Any), ("covers", _data.covers as Any)])
|
||||
case .stickerSetNoCovered(let _data):
|
||||
return ("stickerSetNoCovered", [("set", _data.set as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_stickerSetCovered(_ reader: BufferReader) -> StickerSetCovered? {
|
||||
var _1: Api.StickerSet?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.StickerSet
|
||||
}
|
||||
var _2: Api.Document?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.Document
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.StickerSetCovered.stickerSetCovered(Cons_stickerSetCovered(set: _1!, cover: _2!))
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_stickerSetFullCovered(_ reader: BufferReader) -> StickerSetCovered? {
|
||||
var _1: Api.StickerSet?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.StickerSet
|
||||
}
|
||||
var _2: [Api.StickerPack]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerPack.self)
|
||||
}
|
||||
var _3: [Api.StickerKeyword]?
|
||||
if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerKeyword.self)
|
||||
}
|
||||
var _4: [Api.Document]?
|
||||
if let _ = reader.readInt32() {
|
||||
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.StickerSetCovered.stickerSetFullCovered(Cons_stickerSetFullCovered(set: _1!, packs: _2!, keywords: _3!, documents: _4!))
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_stickerSetMultiCovered(_ reader: BufferReader) -> StickerSetCovered? {
|
||||
var _1: Api.StickerSet?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.StickerSet
|
||||
}
|
||||
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.StickerSetCovered.stickerSetMultiCovered(Cons_stickerSetMultiCovered(set: _1!, covers: _2!))
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_stickerSetNoCovered(_ reader: BufferReader) -> StickerSetCovered? {
|
||||
var _1: Api.StickerSet?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.StickerSet
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.StickerSetCovered.stickerSetNoCovered(Cons_stickerSetNoCovered(set: _1!))
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,186 @@
|
|||
public extension Api {
|
||||
enum StickerSetCovered: TypeConstructorDescription {
|
||||
public class Cons_stickerSetCovered {
|
||||
public var set: Api.StickerSet
|
||||
public var cover: Api.Document
|
||||
public init(set: Api.StickerSet, cover: Api.Document) {
|
||||
self.set = set
|
||||
self.cover = cover
|
||||
}
|
||||
}
|
||||
public class Cons_stickerSetFullCovered {
|
||||
public var set: Api.StickerSet
|
||||
public var packs: [Api.StickerPack]
|
||||
public var keywords: [Api.StickerKeyword]
|
||||
public var documents: [Api.Document]
|
||||
public init(set: Api.StickerSet, packs: [Api.StickerPack], keywords: [Api.StickerKeyword], documents: [Api.Document]) {
|
||||
self.set = set
|
||||
self.packs = packs
|
||||
self.keywords = keywords
|
||||
self.documents = documents
|
||||
}
|
||||
}
|
||||
public class Cons_stickerSetMultiCovered {
|
||||
public var set: Api.StickerSet
|
||||
public var covers: [Api.Document]
|
||||
public init(set: Api.StickerSet, covers: [Api.Document]) {
|
||||
self.set = set
|
||||
self.covers = covers
|
||||
}
|
||||
}
|
||||
public class Cons_stickerSetNoCovered {
|
||||
public var set: Api.StickerSet
|
||||
public init(set: Api.StickerSet) {
|
||||
self.set = set
|
||||
}
|
||||
}
|
||||
case stickerSetCovered(Cons_stickerSetCovered)
|
||||
case stickerSetFullCovered(Cons_stickerSetFullCovered)
|
||||
case stickerSetMultiCovered(Cons_stickerSetMultiCovered)
|
||||
case stickerSetNoCovered(Cons_stickerSetNoCovered)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .stickerSetCovered(let _data):
|
||||
if boxed {
|
||||
buffer.appendInt32(1678812626)
|
||||
}
|
||||
_data.set.serialize(buffer, true)
|
||||
_data.cover.serialize(buffer, true)
|
||||
break
|
||||
case .stickerSetFullCovered(let _data):
|
||||
if boxed {
|
||||
buffer.appendInt32(1087454222)
|
||||
}
|
||||
_data.set.serialize(buffer, true)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(_data.packs.count))
|
||||
for item in _data.packs {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(_data.keywords.count))
|
||||
for item in _data.keywords {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(_data.documents.count))
|
||||
for item in _data.documents {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
case .stickerSetMultiCovered(let _data):
|
||||
if boxed {
|
||||
buffer.appendInt32(872932635)
|
||||
}
|
||||
_data.set.serialize(buffer, true)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(_data.covers.count))
|
||||
for item in _data.covers {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
case .stickerSetNoCovered(let _data):
|
||||
if boxed {
|
||||
buffer.appendInt32(2008112412)
|
||||
}
|
||||
_data.set.serialize(buffer, true)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .stickerSetCovered(let _data):
|
||||
return ("stickerSetCovered", [("set", _data.set as Any), ("cover", _data.cover as Any)])
|
||||
case .stickerSetFullCovered(let _data):
|
||||
return ("stickerSetFullCovered", [("set", _data.set as Any), ("packs", _data.packs as Any), ("keywords", _data.keywords as Any), ("documents", _data.documents as Any)])
|
||||
case .stickerSetMultiCovered(let _data):
|
||||
return ("stickerSetMultiCovered", [("set", _data.set as Any), ("covers", _data.covers as Any)])
|
||||
case .stickerSetNoCovered(let _data):
|
||||
return ("stickerSetNoCovered", [("set", _data.set as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_stickerSetCovered(_ reader: BufferReader) -> StickerSetCovered? {
|
||||
var _1: Api.StickerSet?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.StickerSet
|
||||
}
|
||||
var _2: Api.Document?
|
||||
if let signature = reader.readInt32() {
|
||||
_2 = Api.parse(reader, signature: signature) as? Api.Document
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
if _c1 && _c2 {
|
||||
return Api.StickerSetCovered.stickerSetCovered(Cons_stickerSetCovered(set: _1!, cover: _2!))
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_stickerSetFullCovered(_ reader: BufferReader) -> StickerSetCovered? {
|
||||
var _1: Api.StickerSet?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.StickerSet
|
||||
}
|
||||
var _2: [Api.StickerPack]?
|
||||
if let _ = reader.readInt32() {
|
||||
_2 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerPack.self)
|
||||
}
|
||||
var _3: [Api.StickerKeyword]?
|
||||
if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.StickerKeyword.self)
|
||||
}
|
||||
var _4: [Api.Document]?
|
||||
if let _ = reader.readInt32() {
|
||||
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
let _c4 = _4 != nil
|
||||
if _c1 && _c2 && _c3 && _c4 {
|
||||
return Api.StickerSetCovered.stickerSetFullCovered(Cons_stickerSetFullCovered(set: _1!, packs: _2!, keywords: _3!, documents: _4!))
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_stickerSetMultiCovered(_ reader: BufferReader) -> StickerSetCovered? {
|
||||
var _1: Api.StickerSet?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.StickerSet
|
||||
}
|
||||
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.StickerSetCovered.stickerSetMultiCovered(Cons_stickerSetMultiCovered(set: _1!, covers: _2!))
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
public static func parse_stickerSetNoCovered(_ reader: BufferReader) -> StickerSetCovered? {
|
||||
var _1: Api.StickerSet?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.StickerSet
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
if _c1 {
|
||||
return Api.StickerSetCovered.stickerSetNoCovered(Cons_stickerSetNoCovered(set: _1!))
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum StoriesStealthMode: TypeConstructorDescription {
|
||||
public class Cons_storiesStealthMode {
|
||||
|
|
@ -1532,64 +1715,3 @@ public extension Api {
|
|||
}
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
enum TopPeerCategoryPeers: TypeConstructorDescription {
|
||||
public class Cons_topPeerCategoryPeers {
|
||||
public var category: Api.TopPeerCategory
|
||||
public var count: Int32
|
||||
public var peers: [Api.TopPeer]
|
||||
public init(category: Api.TopPeerCategory, count: Int32, peers: [Api.TopPeer]) {
|
||||
self.category = category
|
||||
self.count = count
|
||||
self.peers = peers
|
||||
}
|
||||
}
|
||||
case topPeerCategoryPeers(Cons_topPeerCategoryPeers)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .topPeerCategoryPeers(let _data):
|
||||
if boxed {
|
||||
buffer.appendInt32(-75283823)
|
||||
}
|
||||
_data.category.serialize(buffer, true)
|
||||
serializeInt32(_data.count, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(_data.peers.count))
|
||||
for item in _data.peers {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .topPeerCategoryPeers(let _data):
|
||||
return ("topPeerCategoryPeers", [("category", _data.category as Any), ("count", _data.count as Any), ("peers", _data.peers as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_topPeerCategoryPeers(_ reader: BufferReader) -> TopPeerCategoryPeers? {
|
||||
var _1: Api.TopPeerCategory?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.TopPeerCategory
|
||||
}
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: [Api.TopPeer]?
|
||||
if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.TopPeer.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.TopPeerCategoryPeers.topPeerCategoryPeers(Cons_topPeerCategoryPeers(category: _1!, count: _2!, peers: _3!))
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,3 +1,64 @@
|
|||
public extension Api {
|
||||
enum TopPeerCategoryPeers: TypeConstructorDescription {
|
||||
public class Cons_topPeerCategoryPeers {
|
||||
public var category: Api.TopPeerCategory
|
||||
public var count: Int32
|
||||
public var peers: [Api.TopPeer]
|
||||
public init(category: Api.TopPeerCategory, count: Int32, peers: [Api.TopPeer]) {
|
||||
self.category = category
|
||||
self.count = count
|
||||
self.peers = peers
|
||||
}
|
||||
}
|
||||
case topPeerCategoryPeers(Cons_topPeerCategoryPeers)
|
||||
|
||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||
switch self {
|
||||
case .topPeerCategoryPeers(let _data):
|
||||
if boxed {
|
||||
buffer.appendInt32(-75283823)
|
||||
}
|
||||
_data.category.serialize(buffer, true)
|
||||
serializeInt32(_data.count, buffer: buffer, boxed: false)
|
||||
buffer.appendInt32(481674261)
|
||||
buffer.appendInt32(Int32(_data.peers.count))
|
||||
for item in _data.peers {
|
||||
item.serialize(buffer, true)
|
||||
}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||
switch self {
|
||||
case .topPeerCategoryPeers(let _data):
|
||||
return ("topPeerCategoryPeers", [("category", _data.category as Any), ("count", _data.count as Any), ("peers", _data.peers as Any)])
|
||||
}
|
||||
}
|
||||
|
||||
public static func parse_topPeerCategoryPeers(_ reader: BufferReader) -> TopPeerCategoryPeers? {
|
||||
var _1: Api.TopPeerCategory?
|
||||
if let signature = reader.readInt32() {
|
||||
_1 = Api.parse(reader, signature: signature) as? Api.TopPeerCategory
|
||||
}
|
||||
var _2: Int32?
|
||||
_2 = reader.readInt32()
|
||||
var _3: [Api.TopPeer]?
|
||||
if let _ = reader.readInt32() {
|
||||
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.TopPeer.self)
|
||||
}
|
||||
let _c1 = _1 != nil
|
||||
let _c2 = _2 != nil
|
||||
let _c3 = _3 != nil
|
||||
if _c1 && _c2 && _c3 {
|
||||
return Api.TopPeerCategoryPeers.topPeerCategoryPeers(Cons_topPeerCategoryPeers(category: _1!, count: _2!, peers: _3!))
|
||||
}
|
||||
else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
public extension Api {
|
||||
indirect enum Update: TypeConstructorDescription {
|
||||
public class Cons_updateBotBusinessConnect {
|
||||
|
|
|
|||
|
|
@ -248,7 +248,7 @@ func telegramMediaActionFromApiAction(_ action: Api.MessageAction) -> TelegramMe
|
|||
guard let gift = StarGift(apiStarGift: apiGift) else {
|
||||
return nil
|
||||
}
|
||||
return TelegramMediaAction(action: .starGiftUnique(gift: gift, isUpgrade: (flags & (1 << 0)) != 0, isTransferred: (flags & (1 << 1)) != 0, savedToProfile: (flags & (1 << 2)) != 0, canExportDate: canExportAt, transferStars: transferStars, isRefunded: (flags & (1 << 5)) != 0, isPrepaidUpgrade: (flags & (1 << 11)) != 0, peerId: peer?.peerId, senderId: fromId?.peerId, savedId: savedId, resaleAmount: resaleAmount.flatMap { CurrencyAmount(apiAmount: $0) }, canTransferDate: canTransferDate, canResaleDate: canResaleDate, dropOriginalDetailsStars: dropOriginalDetailsStars, assigned: (flags & (1 << 13)) != 0, fromOffer: (flags & (1 << 14)) != 0, canCraftAt: canCraftAt))
|
||||
return TelegramMediaAction(action: .starGiftUnique(gift: gift, isUpgrade: (flags & (1 << 0)) != 0, isTransferred: (flags & (1 << 1)) != 0, savedToProfile: (flags & (1 << 2)) != 0, canExportDate: canExportAt, transferStars: transferStars, isRefunded: (flags & (1 << 5)) != 0, isPrepaidUpgrade: (flags & (1 << 11)) != 0, peerId: peer?.peerId, senderId: fromId?.peerId, savedId: savedId, resaleAmount: resaleAmount.flatMap { CurrencyAmount(apiAmount: $0) }, canTransferDate: canTransferDate, canResaleDate: canResaleDate, dropOriginalDetailsStars: dropOriginalDetailsStars, assigned: (flags & (1 << 13)) != 0, fromOffer: (flags & (1 << 14)) != 0, canCraftAt: canCraftAt, isCrafted: (flags & (1 << 16)) != 0))
|
||||
case let .messageActionPaidMessagesRefunded(messageActionPaidMessagesRefundedData):
|
||||
let (count, stars) = (messageActionPaidMessagesRefundedData.count, messageActionPaidMessagesRefundedData.stars)
|
||||
return TelegramMediaAction(action: .paidMessagesRefunded(count: count, stars: stars))
|
||||
|
|
|
|||
|
|
@ -256,7 +256,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
|||
case giftStars(currency: String, amount: Int64, count: Int64, cryptoCurrency: String?, cryptoAmount: Int64?, transactionId: String?)
|
||||
case prizeStars(amount: Int64, isUnclaimed: Bool, boostPeerId: PeerId?, transactionId: String?, giveawayMessageId: MessageId?)
|
||||
case starGift(gift: StarGift, convertStars: Int64?, text: String?, entities: [MessageTextEntity]?, nameHidden: Bool, savedToProfile: Bool, converted: Bool, upgraded: Bool, canUpgrade: Bool, upgradeStars: Int64?, isRefunded: Bool, isPrepaidUpgrade: Bool, upgradeMessageId: Int32?, peerId: EnginePeer.Id?, senderId: EnginePeer.Id?, savedId: Int64?, prepaidUpgradeHash: String?, giftMessageId: Int32?, upgradeSeparate: Bool, isAuctionAcquired: Bool, toPeerId: EnginePeer.Id?, number: Int32?)
|
||||
case starGiftUnique(gift: StarGift, isUpgrade: Bool, isTransferred: Bool, savedToProfile: Bool, canExportDate: Int32?, transferStars: Int64?, isRefunded: Bool, isPrepaidUpgrade: Bool, peerId: EnginePeer.Id?, senderId: EnginePeer.Id?, savedId: Int64?, resaleAmount: CurrencyAmount?, canTransferDate: Int32?, canResaleDate: Int32?, dropOriginalDetailsStars: Int64?, assigned: Bool, fromOffer: Bool, canCraftAt: Int32?)
|
||||
case starGiftUnique(gift: StarGift, isUpgrade: Bool, isTransferred: Bool, savedToProfile: Bool, canExportDate: Int32?, transferStars: Int64?, isRefunded: Bool, isPrepaidUpgrade: Bool, peerId: EnginePeer.Id?, senderId: EnginePeer.Id?, savedId: Int64?, resaleAmount: CurrencyAmount?, canTransferDate: Int32?, canResaleDate: Int32?, dropOriginalDetailsStars: Int64?, assigned: Bool, fromOffer: Bool, canCraftAt: Int32?, isCrafted: Bool)
|
||||
case paidMessagesRefunded(count: Int32, stars: Int64)
|
||||
case paidMessagesPriceEdited(stars: Int64, broadcastMessagesAllowed: Bool)
|
||||
case conferenceCall(ConferenceCall)
|
||||
|
|
@ -404,7 +404,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
|||
} else if let stars = decoder.decodeOptionalInt64ForKey("resaleStars") {
|
||||
resaleAmount = CurrencyAmount(amount: StarsAmount(value: stars, nanos: 0), currency: .stars)
|
||||
}
|
||||
self = .starGiftUnique(gift: decoder.decodeObjectForKey("gift", decoder: { StarGift(decoder: $0) }) as! StarGift, isUpgrade: decoder.decodeBoolForKey("isUpgrade", orElse: false), isTransferred: decoder.decodeBoolForKey("isTransferred", orElse: false), savedToProfile: decoder.decodeBoolForKey("savedToProfile", orElse: false), canExportDate: decoder.decodeOptionalInt32ForKey("canExportDate"), transferStars: decoder.decodeOptionalInt64ForKey("transferStars"), isRefunded: decoder.decodeBoolForKey("isRefunded", orElse: false), isPrepaidUpgrade: decoder.decodeBoolForKey("isPrepaidUpgrade", orElse: false), peerId: decoder.decodeOptionalInt64ForKey("peerId").flatMap { EnginePeer.Id($0) }, senderId: decoder.decodeOptionalInt64ForKey("senderId").flatMap { EnginePeer.Id($0) }, savedId: decoder.decodeOptionalInt64ForKey("savedId"), resaleAmount: resaleAmount, canTransferDate: decoder.decodeOptionalInt32ForKey("canTransferDate"), canResaleDate: decoder.decodeOptionalInt32ForKey("canResaleDate"), dropOriginalDetailsStars: decoder.decodeOptionalInt64ForKey("dropOriginalDetailsStars"), assigned: decoder.decodeBoolForKey("assigned", orElse: false), fromOffer: decoder.decodeBoolForKey("fromOffer", orElse: false), canCraftAt: decoder.decodeOptionalInt32ForKey("canCraftAt"))
|
||||
self = .starGiftUnique(gift: decoder.decodeObjectForKey("gift", decoder: { StarGift(decoder: $0) }) as! StarGift, isUpgrade: decoder.decodeBoolForKey("isUpgrade", orElse: false), isTransferred: decoder.decodeBoolForKey("isTransferred", orElse: false), savedToProfile: decoder.decodeBoolForKey("savedToProfile", orElse: false), canExportDate: decoder.decodeOptionalInt32ForKey("canExportDate"), transferStars: decoder.decodeOptionalInt64ForKey("transferStars"), isRefunded: decoder.decodeBoolForKey("isRefunded", orElse: false), isPrepaidUpgrade: decoder.decodeBoolForKey("isPrepaidUpgrade", orElse: false), peerId: decoder.decodeOptionalInt64ForKey("peerId").flatMap { EnginePeer.Id($0) }, senderId: decoder.decodeOptionalInt64ForKey("senderId").flatMap { EnginePeer.Id($0) }, savedId: decoder.decodeOptionalInt64ForKey("savedId"), resaleAmount: resaleAmount, canTransferDate: decoder.decodeOptionalInt32ForKey("canTransferDate"), canResaleDate: decoder.decodeOptionalInt32ForKey("canResaleDate"), dropOriginalDetailsStars: decoder.decodeOptionalInt64ForKey("dropOriginalDetailsStars"), assigned: decoder.decodeBoolForKey("assigned", orElse: false), fromOffer: decoder.decodeBoolForKey("fromOffer", orElse: false), canCraftAt: decoder.decodeOptionalInt32ForKey("canCraftAt"), isCrafted: decoder.decodeBoolForKey("isCrafted", orElse: false))
|
||||
case 46:
|
||||
self = .paidMessagesRefunded(count: decoder.decodeInt32ForKey("count", orElse: 0), stars: decoder.decodeInt64ForKey("stars", orElse: 0))
|
||||
case 47:
|
||||
|
|
@ -803,7 +803,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
|||
} else {
|
||||
encoder.encodeNil(forKey: "number")
|
||||
}
|
||||
case let .starGiftUnique(gift, isUpgrade, isTransferred, savedToProfile, canExportDate, transferStars, isRefunded, isPrepaidUpgrade, peerId, senderId, savedId, resaleAmount, canTransferDate, canResaleDate, dropOriginalDetailsStars, assigned, fromOffer, canCraftAt):
|
||||
case let .starGiftUnique(gift, isUpgrade, isTransferred, savedToProfile, canExportDate, transferStars, isRefunded, isPrepaidUpgrade, peerId, senderId, savedId, resaleAmount, canTransferDate, canResaleDate, dropOriginalDetailsStars, assigned, fromOffer, canCraftAt, isCrafted):
|
||||
encoder.encodeInt32(45, forKey: "_rawValue")
|
||||
encoder.encodeObject(gift, forKey: "gift")
|
||||
encoder.encodeBool(isUpgrade, forKey: "isUpgrade")
|
||||
|
|
@ -864,6 +864,8 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
|||
} else {
|
||||
encoder.encodeNil(forKey: "canCraftAt")
|
||||
}
|
||||
|
||||
encoder.encodeBool(isCrafted, forKey: "isCrafted")
|
||||
case let .paidMessagesRefunded(count, stars):
|
||||
encoder.encodeInt32(46, forKey: "_rawValue")
|
||||
encoder.encodeInt32(count, forKey: "count")
|
||||
|
|
@ -971,7 +973,7 @@ public enum TelegramMediaActionType: PostboxCoding, Equatable {
|
|||
peerIds.append(toPeerId)
|
||||
}
|
||||
return peerIds
|
||||
case let .starGiftUnique(gift, _, _, _, _, _, _, _, peerId, senderId, _, _, _, _, _, _, _, _):
|
||||
case let .starGiftUnique(gift, _, _, _, _, _, _, _, peerId, senderId, _, _, _, _, _, _, _, _, _):
|
||||
var peerIds: [PeerId] = []
|
||||
if let peerId {
|
||||
peerIds.append(peerId)
|
||||
|
|
|
|||
|
|
@ -460,12 +460,13 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||
case rare
|
||||
case epic
|
||||
case legendary
|
||||
case uncommon
|
||||
|
||||
public var permilleValue: Int32 {
|
||||
switch self {
|
||||
case let .permille(value):
|
||||
return value
|
||||
case .rare, .epic, .legendary:
|
||||
case .rare, .epic, .legendary, .uncommon:
|
||||
return 0
|
||||
}
|
||||
}
|
||||
|
|
@ -501,6 +502,8 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||
return .epic
|
||||
case 3:
|
||||
return .legendary
|
||||
case 4:
|
||||
return .uncommon
|
||||
default:
|
||||
let permille = try container.decodeIfPresent(Int32.self, forKey: .rarity) ?? 0
|
||||
return .permille(permille)
|
||||
|
|
@ -555,6 +558,8 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||
return .epic
|
||||
case 3:
|
||||
return .legendary
|
||||
case 4:
|
||||
return .uncommon
|
||||
default:
|
||||
let permille = decoder.decodeInt32ForKey(CodingKeys.rarity.rawValue, orElse: 0)
|
||||
return .permille(permille)
|
||||
|
|
@ -614,6 +619,8 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||
try container.encode(Int32(2), forKey: .rarityType)
|
||||
case .legendary:
|
||||
try container.encode(Int32(3), forKey: .rarityType)
|
||||
case .uncommon:
|
||||
try container.encode(Int32(4), forKey: .rarityType)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -660,6 +667,8 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||
encoder.encodeInt32(2, forKey: CodingKeys.rarityType.rawValue)
|
||||
case .legendary:
|
||||
encoder.encodeInt32(3, forKey: CodingKeys.rarityType.rawValue)
|
||||
case .uncommon:
|
||||
encoder.encodeInt32(4, forKey: CodingKeys.rarityType.rawValue)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -772,7 +781,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||
public let title: String
|
||||
public let number: Int32
|
||||
public let slug: String
|
||||
public let owner: Owner
|
||||
public let owner: Owner?
|
||||
public let attributes: [Attribute]
|
||||
public let availability: Availability
|
||||
public let giftAddress: String?
|
||||
|
|
@ -789,7 +798,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||
public let minOfferStars: Int64?
|
||||
public let craftChancePermille: Int32?
|
||||
|
||||
public init(id: Int64, giftId: Int64, title: String, number: Int32, slug: String, owner: Owner, attributes: [Attribute], availability: Availability, giftAddress: String?, resellAmounts: [CurrencyAmount]?, resellForTonOnly: Bool, releasedBy: EnginePeer.Id?, valueAmount: Int64?, valueCurrency: String?, valueUsdAmount: Int64?, flags: Flags, themePeerId: EnginePeer.Id?, peerColor: PeerCollectibleColor?, hostPeerId: EnginePeer.Id?, minOfferStars: Int64?, craftChancePermille: Int32?) {
|
||||
public init(id: Int64, giftId: Int64, title: String, number: Int32, slug: String, owner: Owner?, attributes: [Attribute], availability: Availability, giftAddress: String?, resellAmounts: [CurrencyAmount]?, resellForTonOnly: Bool, releasedBy: EnginePeer.Id?, valueAmount: Int64?, valueCurrency: String?, valueUsdAmount: Int64?, flags: Flags, themePeerId: EnginePeer.Id?, peerColor: PeerCollectibleColor?, hostPeerId: EnginePeer.Id?, minOfferStars: Int64?, craftChancePermille: Int32?) {
|
||||
self.id = id
|
||||
self.giftId = giftId
|
||||
self.title = title
|
||||
|
|
@ -827,7 +836,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||
} else if let ownerName = try container.decodeIfPresent(String.self, forKey: .ownerName) {
|
||||
self.owner = .name(ownerName)
|
||||
} else {
|
||||
self.owner = .name("Unknown")
|
||||
self.owner = nil
|
||||
}
|
||||
self.attributes = try container.decode([UniqueGift.Attribute].self, forKey: .attributes)
|
||||
self.availability = try container.decode(UniqueGift.Availability.self, forKey: .availability)
|
||||
|
|
@ -865,7 +874,7 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||
} else if let ownerName = decoder.decodeOptionalStringForKey(CodingKeys.ownerName.rawValue) {
|
||||
self.owner = .name(ownerName)
|
||||
} else {
|
||||
self.owner = .name("Unknown")
|
||||
self.owner = nil
|
||||
}
|
||||
self.attributes = (try? decoder.decodeObjectArrayWithCustomDecoderForKey(CodingKeys.attributes.rawValue, decoder: { UniqueGift.Attribute(decoder: $0) })) ?? []
|
||||
self.availability = decoder.decodeObjectForKey(CodingKeys.availability.rawValue, decoder: { UniqueGift.Availability(decoder: $0) }) as! UniqueGift.Availability
|
||||
|
|
@ -904,6 +913,8 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||
try container.encode(name, forKey: .ownerName)
|
||||
case let .address(address):
|
||||
try container.encode(address, forKey: .ownerAddress)
|
||||
default:
|
||||
break
|
||||
}
|
||||
try container.encode(self.attributes, forKey: .attributes)
|
||||
try container.encode(self.availability, forKey: .availability)
|
||||
|
|
@ -935,6 +946,8 @@ public enum StarGift: Equatable, Codable, PostboxCoding {
|
|||
encoder.encodeString(name, forKey: CodingKeys.ownerName.rawValue)
|
||||
case let .address(address):
|
||||
encoder.encodeString(address, forKey: CodingKeys.ownerAddress.rawValue)
|
||||
default:
|
||||
break
|
||||
}
|
||||
encoder.encodeObjectArray(self.attributes, forKey: CodingKeys.attributes.rawValue)
|
||||
encoder.encodeObject(self.availability, forKey: CodingKeys.availability.rawValue)
|
||||
|
|
@ -1257,7 +1270,7 @@ extension StarGift {
|
|||
))
|
||||
case let .starGiftUnique(starGiftUniqueData):
|
||||
let (apiFlags, id, giftId, title, slug, num, ownerPeerId, ownerName, ownerAddress, attributes, availabilityIssued, availabilityTotal, giftAddress, apiResellAmount, releasedBy, valueAmount, valueCurrency, valueUsdAmount, themePeer, peerColor, hostPeerId, minOfferStars, craftChancePermille) = (starGiftUniqueData.flags, starGiftUniqueData.id, starGiftUniqueData.giftId, starGiftUniqueData.title, starGiftUniqueData.slug, starGiftUniqueData.num, starGiftUniqueData.ownerId, starGiftUniqueData.ownerName, starGiftUniqueData.ownerAddress, starGiftUniqueData.attributes, starGiftUniqueData.availabilityIssued, starGiftUniqueData.availabilityTotal, starGiftUniqueData.giftAddress, starGiftUniqueData.resellAmount, starGiftUniqueData.releasedBy, starGiftUniqueData.valueAmount, starGiftUniqueData.valueCurrency, starGiftUniqueData.valueUsdAmount, starGiftUniqueData.themePeer, starGiftUniqueData.peerColor, starGiftUniqueData.hostId, starGiftUniqueData.offerMinStars, starGiftUniqueData.craftChancePermille)
|
||||
let owner: StarGift.UniqueGift.Owner
|
||||
let owner: StarGift.UniqueGift.Owner?
|
||||
if let ownerAddress {
|
||||
owner = .address(ownerAddress)
|
||||
} else if let ownerId = ownerPeerId?.peerId {
|
||||
|
|
@ -1265,7 +1278,7 @@ extension StarGift {
|
|||
} else if let ownerName {
|
||||
owner = .name(ownerName)
|
||||
} else {
|
||||
return nil
|
||||
owner = nil
|
||||
}
|
||||
let resellAmounts = apiResellAmount?.compactMap { CurrencyAmount(apiAmount: $0) }
|
||||
var flags = StarGift.UniqueGift.Flags()
|
||||
|
|
@ -1296,8 +1309,7 @@ extension StarGift {
|
|||
number: num,
|
||||
slug: slug,
|
||||
owner: owner,
|
||||
attributes: attributes.compactMap { UniqueGift.Attribute(apiAttribute: $0)
|
||||
},
|
||||
attributes: attributes.compactMap { UniqueGift.Attribute(apiAttribute: $0) },
|
||||
availability: UniqueGift.Availability(issued: availabilityIssued, total: availabilityTotal),
|
||||
giftAddress: giftAddress,
|
||||
resellAmounts: resellAmounts,
|
||||
|
|
@ -1312,7 +1324,7 @@ extension StarGift {
|
|||
hostPeerId: hostPeerId?.peerId,
|
||||
minOfferStars: minOfferStars.flatMap { Int64($0) },
|
||||
craftChancePermille: craftChancePermille
|
||||
))
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1524,7 +1536,7 @@ func _internal_dropStarGiftOriginalDetails(account: Account, reference: StarGift
|
|||
storeForwardInfo = StoreMessageForwardInfo(authorId: forwardInfo.author?.id, sourceId: forwardInfo.source?.id, sourceMessageId: forwardInfo.sourceMessageId, date: forwardInfo.date, authorSignature: forwardInfo.authorSignature, psaType: forwardInfo.psaType, flags: forwardInfo.flags)
|
||||
}
|
||||
var media = currentMessage.media
|
||||
if let action = media.first(where: { $0 is TelegramMediaAction }) as? TelegramMediaAction, case let .starGiftUnique(gift, isUpgrade, isTransferred, savedToProfile, canExportDate, transferStars, isRefunded, isPrepaidUpgrade, peerId, senderId, savedId, resaleAmount, canTransferDate, canResaleDate, _, assigned, fromOffer, canCraftAt) = action.action, case let .unique(uniqueGift) = gift {
|
||||
if let action = media.first(where: { $0 is TelegramMediaAction }) as? TelegramMediaAction, case let .starGiftUnique(gift, isUpgrade, isTransferred, savedToProfile, canExportDate, transferStars, isRefunded, isPrepaidUpgrade, peerId, senderId, savedId, resaleAmount, canTransferDate, canResaleDate, _, assigned, fromOffer, canCraftAt, isCrafted) = action.action, case let .unique(uniqueGift) = gift {
|
||||
let updatedAttributes = uniqueGift.attributes.filter { $0.attributeType != .originalInfo }
|
||||
media = [
|
||||
TelegramMediaAction(
|
||||
|
|
@ -1546,7 +1558,8 @@ func _internal_dropStarGiftOriginalDetails(account: Account, reference: StarGift
|
|||
dropOriginalDetailsStars: nil,
|
||||
assigned: assigned,
|
||||
fromOffer: fromOffer,
|
||||
canCraftAt: canCraftAt
|
||||
canCraftAt: canCraftAt,
|
||||
isCrafted: isCrafted
|
||||
)
|
||||
)
|
||||
]
|
||||
|
|
@ -1653,7 +1666,7 @@ func _internal_upgradeStarGift(account: Account, formId: Int64?, reference: Star
|
|||
let message = updateNewMessageData.message
|
||||
if let message = StoreMessage(apiMessage: message, accountPeerId: account.peerId, peerIsForum: false) {
|
||||
for media in message.media {
|
||||
if let action = media as? TelegramMediaAction, case let .starGiftUnique(gift, _, _, savedToProfile, canExportDate, transferStars, _, _, peerId, _, savedId, _, canTransferDate, canResaleDate, dropOriginalDetailsStars, _, _, canCraftAt) = action.action, case let .Id(messageId) = message.id {
|
||||
if let action = media as? TelegramMediaAction, case let .starGiftUnique(gift, _, _, savedToProfile, canExportDate, transferStars, _, _, peerId, _, savedId, _, canTransferDate, canResaleDate, dropOriginalDetailsStars, _, _, canCraftAt, _) = action.action, case let .Id(messageId) = message.id {
|
||||
let reference: StarGiftReference
|
||||
if let peerId, let savedId {
|
||||
reference = .peer(peerId: peerId, id: savedId)
|
||||
|
|
@ -2370,12 +2383,16 @@ private final class ProfileGiftsContextImpl {
|
|||
}
|
||||
|
||||
func removeStarGifts(references: [StarGiftReference]) {
|
||||
self.gifts.removeAll(where: {
|
||||
if let reference = $0.reference {
|
||||
return references.contains(reference)
|
||||
} else {
|
||||
return false
|
||||
self.gifts.removeAll(where: { gift in
|
||||
if let reference = gift.reference, references.contains(reference) {
|
||||
return true
|
||||
}
|
||||
for reference in references {
|
||||
if case let .slug(slug) = reference, case let .unique(uniqueGift) = gift.gift, uniqueGift.slug == slug {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
self.pushState()
|
||||
|
||||
|
|
@ -2390,13 +2407,17 @@ private final class ProfileGiftsContextImpl {
|
|||
} else {
|
||||
updatedGifts = []
|
||||
}
|
||||
updatedGifts = updatedGifts.filter { gift in
|
||||
if let reference = gift.reference {
|
||||
return !references.contains(reference)
|
||||
} else {
|
||||
updatedGifts.removeAll(where: { gift in
|
||||
if let reference = gift.reference, references.contains(reference) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
for reference in references {
|
||||
if case let .slug(slug) = reference, case let .unique(uniqueGift) = gift.gift, uniqueGift.slug == slug {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
})
|
||||
updatedCount -= Int32(references.count)
|
||||
if let entry = CodableEntry(CachedProfileGifts(gifts: updatedGifts, count: updatedCount, notificationsEnabled: nil)) {
|
||||
transaction.putItemCacheEntry(id: giftsEntryId(peerId: peerId, collectionId: collectionId), entry: entry)
|
||||
|
|
@ -3186,12 +3207,6 @@ private final class CraftGiftsContextImpl {
|
|||
return _internal_craftStarGift(account: self.account, references: references)
|
||||
}
|
||||
|
||||
func addGift(gift: ProfileGiftsContext.State.StarGift) {
|
||||
self.gifts.insert(gift, at: 0)
|
||||
self.count = self.count + 1
|
||||
self.pushState()
|
||||
}
|
||||
|
||||
func removeGifts(references: [StarGiftReference]) {
|
||||
let referencesSet = Set(references)
|
||||
self.gifts.removeAll { gift in
|
||||
|
|
@ -3325,12 +3340,6 @@ public final class CraftGiftsContext {
|
|||
}
|
||||
}
|
||||
|
||||
public func addGift(gift: ProfileGiftsContext.State.StarGift) {
|
||||
self.impl.with { impl in
|
||||
impl.addGift(gift: gift)
|
||||
}
|
||||
}
|
||||
|
||||
public func removeGifts(references: [StarGiftReference]) {
|
||||
self.impl.with { impl in
|
||||
impl.removeGifts(references: references)
|
||||
|
|
@ -3395,7 +3404,7 @@ func _internal_craftStarGift(account: Account, references: [StarGiftReference])
|
|||
let message = updateNewMessageData.message
|
||||
if let message = StoreMessage(apiMessage: message, accountPeerId: account.peerId, peerIsForum: false) {
|
||||
for media in message.media {
|
||||
if let action = media as? TelegramMediaAction, case let .starGiftUnique(gift, _, _, savedToProfile, canExportDate, transferStars, _, _, peerId, _, savedId, _, canTransferDate, canResaleDate, dropOriginalDetailsStars, _, _, canCraftAt) = action.action, case let .Id(messageId) = message.id {
|
||||
if let action = media as? TelegramMediaAction, case let .starGiftUnique(gift, _, _, savedToProfile, canExportDate, transferStars, _, _, peerId, _, savedId, _, canTransferDate, canResaleDate, dropOriginalDetailsStars, _, _, canCraftAt, _) = action.action, case let .Id(messageId) = message.id {
|
||||
let reference: StarGiftReference
|
||||
if let peerId, let savedId {
|
||||
reference = .peer(peerId: peerId, id: savedId)
|
||||
|
|
@ -3514,6 +3523,8 @@ extension StarGift.UniqueGift.Attribute {
|
|||
return .epic
|
||||
case .starGiftAttributeRarityLegendary:
|
||||
return .legendary
|
||||
case .starGiftAttributeRarityUncommon:
|
||||
return .uncommon
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1658,7 +1658,7 @@ func _internal_sendStarsPaymentForm(account: Account, formId: Int64, source: Bot
|
|||
case .giftCode, .stars, .starsGift, .starsChatSubscription, .starGift, .starGiftUpgrade, .starGiftTransfer, .premiumGift, .starGiftResale, .starGiftPrepaidUpgrade, .starGiftDropOriginalDetails, .starGiftAuctionBid:
|
||||
receiptMessageId = nil
|
||||
}
|
||||
} else if case let .starGiftUnique(gift, _, _, savedToProfile, canExportDate, transferStars, isRefunded, _, peerId, _, savedId, _, canTransferDate, canResaleDate, dropOriginalDetailsStars, _, _, canCraftAt) = action.action, case let .Id(messageId) = message.id {
|
||||
} else if case let .starGiftUnique(gift, _, _, savedToProfile, canExportDate, transferStars, isRefunded, _, peerId, _, savedId, _, canTransferDate, canResaleDate, dropOriginalDetailsStars, _, _, canCraftAt, _) = action.action, case let .Id(messageId) = message.id {
|
||||
let reference: StarGiftReference
|
||||
if let peerId, let savedId {
|
||||
reference = .peer(peerId: peerId, id: savedId)
|
||||
|
|
|
|||
|
|
@ -217,9 +217,10 @@ public final class InitialPresentationDataAndSettings {
|
|||
public let mediaInputSettings: MediaInputSettings
|
||||
public let mediaDisplaySettings: MediaDisplaySettings
|
||||
public let stickerSettings: StickerSettings
|
||||
public let chatSettings: ChatSettings
|
||||
public let experimentalUISettings: ExperimentalUISettings
|
||||
|
||||
public init(presentationData: PresentationData, automaticMediaDownloadSettings: MediaAutoDownloadSettings, autodownloadSettings: AutodownloadSettings, callListSettings: CallListSettings, inAppNotificationSettings: InAppNotificationSettings, mediaInputSettings: MediaInputSettings, mediaDisplaySettings: MediaDisplaySettings, stickerSettings: StickerSettings, experimentalUISettings: ExperimentalUISettings) {
|
||||
public init(presentationData: PresentationData, automaticMediaDownloadSettings: MediaAutoDownloadSettings, autodownloadSettings: AutodownloadSettings, callListSettings: CallListSettings, inAppNotificationSettings: InAppNotificationSettings, mediaInputSettings: MediaInputSettings, mediaDisplaySettings: MediaDisplaySettings, stickerSettings: StickerSettings, chatSettings: ChatSettings, experimentalUISettings: ExperimentalUISettings) {
|
||||
self.presentationData = presentationData
|
||||
self.automaticMediaDownloadSettings = automaticMediaDownloadSettings
|
||||
self.autodownloadSettings = autodownloadSettings
|
||||
|
|
@ -228,6 +229,7 @@ public final class InitialPresentationDataAndSettings {
|
|||
self.mediaInputSettings = mediaInputSettings
|
||||
self.mediaDisplaySettings = mediaDisplaySettings
|
||||
self.stickerSettings = stickerSettings
|
||||
self.chatSettings = chatSettings
|
||||
self.experimentalUISettings = experimentalUISettings
|
||||
}
|
||||
}
|
||||
|
|
@ -245,6 +247,7 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager<Te
|
|||
var experimentalUISettings: PreferencesEntry?
|
||||
var contactSynchronizationSettings: PreferencesEntry?
|
||||
var stickerSettings: PreferencesEntry?
|
||||
var chatSettings: PreferencesEntry?
|
||||
|
||||
init(
|
||||
localizationSettings: PreferencesEntry?,
|
||||
|
|
@ -257,7 +260,8 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager<Te
|
|||
mediaDisplaySettings: PreferencesEntry?,
|
||||
experimentalUISettings: PreferencesEntry?,
|
||||
contactSynchronizationSettings: PreferencesEntry?,
|
||||
stickerSettings: PreferencesEntry?
|
||||
stickerSettings: PreferencesEntry?,
|
||||
chatSettings: PreferencesEntry?
|
||||
) {
|
||||
self.localizationSettings = localizationSettings
|
||||
self.presentationThemeSettings = presentationThemeSettings
|
||||
|
|
@ -270,6 +274,7 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager<Te
|
|||
self.experimentalUISettings = experimentalUISettings
|
||||
self.contactSynchronizationSettings = contactSynchronizationSettings
|
||||
self.stickerSettings = stickerSettings
|
||||
self.chatSettings = chatSettings
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -285,6 +290,7 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager<Te
|
|||
let experimentalUISettings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.experimentalUISettings)
|
||||
let contactSynchronizationSettings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.contactSynchronizationSettings)
|
||||
let stickerSettings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.stickerSettings)
|
||||
let chatSettings = transaction.getSharedData(ApplicationSpecificSharedDataKeys.chatSettings)
|
||||
|
||||
return InternalData(
|
||||
localizationSettings: localizationSettings,
|
||||
|
|
@ -297,7 +303,8 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager<Te
|
|||
mediaDisplaySettings: mediaDisplaySettings,
|
||||
experimentalUISettings: experimentalUISettings,
|
||||
contactSynchronizationSettings: contactSynchronizationSettings,
|
||||
stickerSettings: stickerSettings
|
||||
stickerSettings: stickerSettings,
|
||||
chatSettings: chatSettings
|
||||
)
|
||||
}
|
||||
|> deliverOn(Queue(name: "PresentationData-Load", qos: .userInteractive))
|
||||
|
|
@ -365,6 +372,13 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager<Te
|
|||
stickerSettings = StickerSettings.defaultSettings
|
||||
}
|
||||
|
||||
let chatSettings: ChatSettings
|
||||
if let value = internalData.chatSettings?.get(ChatSettings.self) {
|
||||
chatSettings = value
|
||||
} else {
|
||||
chatSettings = ChatSettings.defaultSettings
|
||||
}
|
||||
|
||||
let experimentalUISettings: ExperimentalUISettings = internalData.experimentalUISettings?.get(ExperimentalUISettings.self) ?? ExperimentalUISettings.defaultSettings
|
||||
|
||||
let contactSettings: ContactSynchronizationSettings = internalData.contactSynchronizationSettings?.get(ContactSynchronizationSettings.self) ?? ContactSynchronizationSettings.defaultSettings
|
||||
|
|
@ -413,7 +427,7 @@ public func currentPresentationDataAndSettings(accountManager: AccountManager<Te
|
|||
|
||||
let chatBubbleCorners = PresentationChatBubbleCorners(mainRadius: CGFloat(themeSettings.chatBubbleSettings.mainRadius), auxiliaryRadius: CGFloat(themeSettings.chatBubbleSettings.auxiliaryRadius), mergeBubbleCorners: themeSettings.chatBubbleSettings.mergeBubbleCorners)
|
||||
|
||||
return InitialPresentationDataAndSettings(presentationData: PresentationData(strings: stringsValue, theme: theme, autoNightModeTriggered: autoNightModeTriggered, chatWallpaper: effectiveChatWallpaper, chatFontSize: chatFontSize, chatBubbleCorners: chatBubbleCorners, listsFontSize: listsFontSize, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder, reduceMotion: themeSettings.reduceMotion, largeEmoji: themeSettings.largeEmoji), automaticMediaDownloadSettings: automaticMediaDownloadSettings, autodownloadSettings: autodownloadSettings, callListSettings: callListSettings, inAppNotificationSettings: inAppNotificationSettings, mediaInputSettings: mediaInputSettings, mediaDisplaySettings: mediaDisplaySettings, stickerSettings: stickerSettings, experimentalUISettings: experimentalUISettings)
|
||||
return InitialPresentationDataAndSettings(presentationData: PresentationData(strings: stringsValue, theme: theme, autoNightModeTriggered: autoNightModeTriggered, chatWallpaper: effectiveChatWallpaper, chatFontSize: chatFontSize, chatBubbleCorners: chatBubbleCorners, listsFontSize: listsFontSize, dateTimeFormat: dateTimeFormat, nameDisplayOrder: nameDisplayOrder, nameSortOrder: nameSortOrder, reduceMotion: themeSettings.reduceMotion, largeEmoji: themeSettings.largeEmoji), automaticMediaDownloadSettings: automaticMediaDownloadSettings, autodownloadSettings: autodownloadSettings, callListSettings: callListSettings, inAppNotificationSettings: inAppNotificationSettings, mediaInputSettings: mediaInputSettings, mediaDisplaySettings: mediaDisplaySettings, stickerSettings: stickerSettings, chatSettings: chatSettings, experimentalUISettings: experimentalUISettings)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1262,7 +1262,7 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
|||
attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_Sent(authorName, starsPrice)._tuple, body: bodyAttributes, argumentAttributes: attributes)
|
||||
}
|
||||
}
|
||||
case let .starGiftUnique(gift, isUpgrade, _, _, _, _, _, isPrepaidUpgrade, peerId, senderId, _, resaleStars, _, _, _, assigned, fromOffer, _):
|
||||
case let .starGiftUnique(gift, isUpgrade, _, _, _, _, _, isPrepaidUpgrade, peerId, senderId, _, resaleStars, _, _, _, assigned, fromOffer, _, isCrafted):
|
||||
if case let .unique(gift) = gift {
|
||||
if !forAdditionalServiceMessage && !"".isEmpty {
|
||||
attributedString = NSAttributedString(string: "\(gift.title) #\(presentationStringsFormattedNumber(gift.number, dateTimeFormat.groupingSeparator))", font: titleFont, textColor: primaryTextColor)
|
||||
|
|
@ -1338,7 +1338,11 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
|||
attributedString = addAttributesToStringWithRanges(strings.Notification_StarsGift_BoughtYou(giftTitle, starsString)._tuple, body: bodyAttributes, argumentAttributes: [0: boldAttributes, 1: boldAttributes])
|
||||
}
|
||||
} else {
|
||||
attributedString = NSAttributedString(string: strings.Notification_StarsGift_TransferYou, font: titleFont, textColor: primaryTextColor)
|
||||
if isCrafted {
|
||||
attributedString = NSAttributedString(string: strings.Notification_StarsGift_Crafted, font: titleFont, textColor: primaryTextColor)
|
||||
} else {
|
||||
attributedString = NSAttributedString(string: strings.Notification_StarsGift_TransferYou, font: titleFont, textColor: primaryTextColor)
|
||||
}
|
||||
}
|
||||
} else if let senderId, let peer = message.peers[senderId] {
|
||||
if let peerId, let targetPeer = message.peers[peerId] {
|
||||
|
|
@ -1776,7 +1780,27 @@ public func universalServiceMessageString(presentationData: (PresentationTheme,
|
|||
rawString = strings.Conversation_EmojiStake_WonYou(value).string
|
||||
}
|
||||
} else {
|
||||
let compactPeerName = message.peers[message.id.peerId].flatMap(EnginePeer.init)?.compactDisplayTitle ?? ""
|
||||
var compactPeerName = ""
|
||||
if let authorSignature = message.forwardInfo?.authorSignature {
|
||||
compactPeerName = authorSignature
|
||||
} else if let author = message.forwardInfo?.author {
|
||||
compactPeerName = EnginePeer(author).compactDisplayTitle
|
||||
} else if let source = message.forwardInfo?.source {
|
||||
compactPeerName = EnginePeer(source).compactDisplayTitle
|
||||
} else {
|
||||
if let author = message.author, case .user = author {
|
||||
compactPeerName = author.compactDisplayTitle
|
||||
} else {
|
||||
for attribute in message.attributes {
|
||||
if let attribute = attribute as? AuthorSignatureMessageAttribute {
|
||||
compactPeerName = attribute.signature
|
||||
}
|
||||
}
|
||||
if compactPeerName.isEmpty {
|
||||
compactPeerName = message.peers[message.id.peerId].flatMap(EnginePeer.init)?.compactDisplayTitle ?? ""
|
||||
}
|
||||
}
|
||||
}
|
||||
if value == 1, let tonAmount = dice.tonAmount {
|
||||
let value = formatTonAmountText(tonAmount, dateTimeFormat: dateTimeFormat)
|
||||
rawString = strings.Conversation_EmojiStake_Lost(compactPeerName, value).string
|
||||
|
|
|
|||
|
|
@ -51,6 +51,8 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
private let context: AccountContext
|
||||
private let subject: MessageActionUrlAuthResult
|
||||
|
||||
var peer: EnginePeer?
|
||||
|
||||
fileprivate var inProgress = false
|
||||
var allowWrite = true
|
||||
weak var controller: ViewController?
|
||||
|
|
@ -58,8 +60,17 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
init(context: AccountContext, subject: MessageActionUrlAuthResult) {
|
||||
self.context = context
|
||||
self.subject = subject
|
||||
|
||||
|
||||
super.init()
|
||||
|
||||
let _ = (context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId))
|
||||
|> deliverOnMainQueue).start(next: { [weak self] peer in
|
||||
guard let self, let peer else {
|
||||
return
|
||||
}
|
||||
self.peer = peer
|
||||
self.updated()
|
||||
})
|
||||
}
|
||||
|
||||
func displayPhoneNumberConfirmation(commit: @escaping (Bool) -> Void) {
|
||||
|
|
@ -78,7 +89,7 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
let alertController = textAlertController(
|
||||
context: self.context,
|
||||
title: "Phone Number",
|
||||
text: "**\(domain)** wants to access your phone number **\(phoneNumber)**. Allow access?",
|
||||
text: "**\(domain)** wants to access your phone number **\(phoneNumber)**.\n\nAllow access?",
|
||||
actions: [
|
||||
TextAlertAction(type: .genericAction, title: "Deny", action: {
|
||||
commit(false)
|
||||
|
|
@ -99,13 +110,12 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
|
||||
static var body: Body {
|
||||
let closeButton = Child(GlassBarButtonComponent.self)
|
||||
let peerButton = Child(AvatarComponent.self)
|
||||
let avatar = Child(AvatarComponent.self)
|
||||
let title = Child(MultilineTextComponent.self)
|
||||
let description = Child(MultilineTextComponent.self)
|
||||
|
||||
let clientSection = Child(ListSectionComponent.self)
|
||||
let optionsSection = Child(ListSectionComponent.self)
|
||||
|
||||
let cancelButton = Child(ButtonComponent.self)
|
||||
let doneButton = Child(ButtonComponent.self)
|
||||
|
||||
|
|
@ -147,6 +157,24 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
availableSize: CGSize(width: 44.0, height: 44.0),
|
||||
transition: .immediate
|
||||
)
|
||||
context.add(closeButton
|
||||
.position(CGPoint(x: 16.0 + closeButton.size.width / 2.0, y: 16.0 + closeButton.size.height / 2.0))
|
||||
)
|
||||
|
||||
if let peer = state.peer {
|
||||
let peerButton = peerButton.update(
|
||||
component: AvatarComponent(
|
||||
context: component.context,
|
||||
theme: environment.theme,
|
||||
peer: peer
|
||||
),
|
||||
availableSize: CGSize(width: 44.0, height: 44.0),
|
||||
transition: .immediate
|
||||
)
|
||||
context.add(peerButton
|
||||
.position(CGPoint(x: context.availableSize.width - 16.0 - peerButton.size.width / 2.0, y: 16.0 + peerButton.size.height / 2.0))
|
||||
)
|
||||
}
|
||||
|
||||
var contentHeight: CGFloat = 32.0
|
||||
let avatar = avatar.update(
|
||||
|
|
@ -320,7 +348,7 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
contentHeight += clientSection.size.height
|
||||
|
||||
if flags.contains(.requestWriteAccess) {
|
||||
contentHeight += 22.0
|
||||
contentHeight += 38.0
|
||||
|
||||
var optionsSectionItems: [AnyComponentWithIdentity<Empty>] = []
|
||||
optionsSectionItems.append(AnyComponentWithIdentity(id: "allowWrite", component: AnyComponent(ListActionItemComponent(
|
||||
|
|
@ -441,10 +469,6 @@ private final class AuthConfirmationSheetContent: CombinedComponent {
|
|||
contentHeight += doneButton.size.height
|
||||
contentHeight += buttonInsets.bottom
|
||||
|
||||
context.add(closeButton
|
||||
.position(CGPoint(x: 16.0 + closeButton.size.width / 2.0, y: 16.0 + closeButton.size.height / 2.0))
|
||||
)
|
||||
|
||||
return CGSize(width: context.availableSize.width, height: contentHeight)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -466,6 +466,9 @@ public final class ButtonComponent: Component {
|
|||
|
||||
super.init(frame: frame)
|
||||
|
||||
self.button.isExclusiveTouch = true
|
||||
self.layer.rasterizationScale = UIScreenScale
|
||||
|
||||
self.addSubview(self.containerView)
|
||||
self.addSubview(self.button)
|
||||
|
||||
|
|
@ -477,12 +480,24 @@ public final class ButtonComponent: Component {
|
|||
case .glass:
|
||||
let transition = ComponentTransition(animation: .curve(duration: highlighted ? 0.25 : 0.35, curve: .spring))
|
||||
if highlighted {
|
||||
self.layer.shouldRasterize = true
|
||||
|
||||
let highlightedColor = component.background.color.withMultiplied(hue: 1.0, saturation: 0.77, brightness: 1.01)
|
||||
transition.setBackgroundColor(view: self.containerView, color: highlightedColor)
|
||||
transition.setScale(view: self.containerView, scale: 1.05)
|
||||
transition.setScale(view: self.containerView, scale: 1.05, completion: { finished in
|
||||
if finished {
|
||||
self.layer.shouldRasterize = false
|
||||
}
|
||||
})
|
||||
} else {
|
||||
self.layer.shouldRasterize = true
|
||||
|
||||
transition.setBackgroundColor(view: self.containerView, color: component.background.color)
|
||||
transition.setScale(view: self.containerView, scale: 1.0)
|
||||
transition.setScale(view: self.containerView, scale: 1.0, completion: { finished in
|
||||
if finished {
|
||||
self.layer.shouldRasterize = false
|
||||
}
|
||||
})
|
||||
}
|
||||
case .legacy:
|
||||
if highlighted {
|
||||
|
|
@ -598,6 +613,7 @@ public final class ButtonComponent: Component {
|
|||
if contentView.superview == nil {
|
||||
contentTransition = .immediate
|
||||
animateIn = true
|
||||
contentView.layer.rasterizationScale = UIScreenScale
|
||||
contentView.isUserInteractionEnabled = false
|
||||
contentContainerView.addSubview(contentView)
|
||||
|
||||
|
|
@ -609,7 +625,10 @@ public final class ButtonComponent: Component {
|
|||
contentTransition.setAlpha(view: contentView, alpha: contentAlpha)
|
||||
|
||||
if animateIn && previousContentItem != nil && !transition.animation.isImmediate {
|
||||
contentView.layer.animateScale(from: 0.4, to: 1.0, duration: 0.35, timingFunction: kCAMediaTimingFunctionSpring)
|
||||
contentView.layer.shouldRasterize = true
|
||||
contentView.layer.animateScale(from: 0.4, to: 1.0, duration: 0.35, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in
|
||||
contentView.layer.shouldRasterize = false
|
||||
})
|
||||
contentView.layer.animateAlpha(from: 0.0, to: contentAlpha, duration: 0.1)
|
||||
contentView.layer.animatePosition(from: CGPoint(x: 0.0, y: -size.height * 0.15), to: CGPoint(), duration: 0.35, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
||||
}
|
||||
|
|
@ -617,6 +636,7 @@ public final class ButtonComponent: Component {
|
|||
|
||||
if let previousContentItem, let previousContentView = previousContentItem.view.view {
|
||||
if !transition.animation.isImmediate {
|
||||
previousContentView.layer.shouldRasterize = true
|
||||
previousContentView.layer.animateScale(from: 1.0, to: 0.0, duration: 0.35, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false)
|
||||
previousContentView.layer.animateAlpha(from: contentAlpha, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak previousContentView] _ in
|
||||
previousContentView?.removeFromSuperview()
|
||||
|
|
|
|||
|
|
@ -18,7 +18,7 @@
|
|||
@property (nonatomic, copy) bool (^ _Nullable shouldPaste)();
|
||||
@property (nonatomic, copy) bool (^ _Nullable shouldRespondToAction)(SEL _Nullable);
|
||||
@property (nonatomic, copy) ChatInputTextViewImplTargetForAction * _Nullable (^ _Nullable targetForAction)(SEL _Nullable);
|
||||
@property (nonatomic, copy) bool (^ _Nullable shouldReturn)();
|
||||
@property (nonatomic, copy) bool (^ _Nullable shouldReturn)(UIKeyModifierFlags);
|
||||
@property (nonatomic, copy) void (^ _Nullable backspaceWhileEmpty)();
|
||||
@property (nonatomic, copy) void (^ _Nullable dropAutocorrectioniOS16)();
|
||||
|
||||
|
|
|
|||
|
|
@ -136,15 +136,22 @@
|
|||
}
|
||||
|
||||
- (NSArray *)keyCommands {
|
||||
UIKeyCommand *plainReturn = [UIKeyCommand keyCommandWithInput:@"\r" modifierFlags:kNilOptions action:@selector(handlePlainReturn:)];
|
||||
UIKeyCommand *plainReturn = [UIKeyCommand keyCommandWithInput:@"\r" modifierFlags:kNilOptions action:@selector(handleReturn:)];
|
||||
UIKeyCommand *cmdReturn = [UIKeyCommand keyCommandWithInput:@"\r" modifierFlags:UIKeyModifierCommand action:@selector(handleReturn:)];
|
||||
return @[
|
||||
plainReturn
|
||||
plainReturn,
|
||||
cmdReturn
|
||||
];
|
||||
}
|
||||
|
||||
- (void)handlePlainReturn:(id)__unused sender {
|
||||
- (void)handleReturn:(UIKeyCommand *)__unused sender {
|
||||
UIKeyModifierFlags modifierFlags = 0;
|
||||
if ([sender isKindOfClass:[UIKeyCommand class]]) {
|
||||
modifierFlags = sender.modifierFlags;
|
||||
}
|
||||
|
||||
if (_shouldReturn) {
|
||||
_shouldReturn();
|
||||
_shouldReturn(modifierFlags);
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -11,7 +11,7 @@ import TextNodeWithEntities
|
|||
|
||||
public protocol ChatInputTextNodeDelegate: AnyObject {
|
||||
func chatInputTextNodeDidUpdateText()
|
||||
func chatInputTextNodeShouldReturn() -> Bool
|
||||
func chatInputTextNodeShouldReturn(modifierFlags: UIKeyModifierFlags) -> Bool
|
||||
func chatInputTextNodeDidChangeSelection(dueToEditing: Bool)
|
||||
func chatInputTextNodeDidBeginEditing()
|
||||
func chatInputTextNodeDidFinishEditing()
|
||||
|
|
@ -1180,11 +1180,11 @@ public final class ChatInputTextView: ChatInputTextViewImpl, UITextViewDelegate,
|
|||
}
|
||||
return self.customDelegate?.chatInputTextNodeShouldPaste() ?? true
|
||||
}
|
||||
self.shouldReturn = { [weak self] in
|
||||
self.shouldReturn = { [weak self] modifierFlags in
|
||||
guard let self else {
|
||||
return true
|
||||
}
|
||||
return self.customDelegate?.chatInputTextNodeShouldReturn() ?? true
|
||||
return self.customDelegate?.chatInputTextNodeShouldReturn(modifierFlags: modifierFlags) ?? true
|
||||
}
|
||||
self.backspaceWhileEmpty = { [weak self] in
|
||||
guard let self else {
|
||||
|
|
|
|||
|
|
@ -149,8 +149,7 @@ public func stringForMessageTimestampStatus(accountPeerId: PeerId, message: Mess
|
|||
dayText = strings.Date_ChatDateHeaderYear(monthAtIndex(Int(timeinfo.tm_mon), strings: strings), "\(timeinfo.tm_mday)", "\(1900 + timeinfo.tm_year)").string
|
||||
}
|
||||
dateText = strings.Message_FullDateFormat(dayText, stringForMessageTimestamp(timestamp: timestamp, dateTimeFormat: dateTimeFormat)).string
|
||||
}
|
||||
else if let forwardInfo = message.forwardInfo, forwardInfo.flags.contains(.isImported) {
|
||||
} else if let forwardInfo = message.forwardInfo, forwardInfo.flags.contains(.isImported) {
|
||||
dateText = strings.Message_ImportedDateFormat(dateStringForDay(strings: strings, dateTimeFormat: dateTimeFormat, timestamp: forwardInfo.date), stringForMessageTimestamp(timestamp: forwardInfo.date, dateTimeFormat: dateTimeFormat), dateText).string
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -282,7 +282,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||
switch action.action {
|
||||
case let .starGift(gift, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
releasedBy = gift.releasedBy
|
||||
case let .starGiftUnique(gift, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
case let .starGiftUnique(gift, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _):
|
||||
releasedBy = gift.releasedBy
|
||||
default:
|
||||
break
|
||||
|
|
@ -690,7 +690,7 @@ public class ChatMessageGiftBubbleContentNode: ChatMessageBubbleContentNode {
|
|||
}
|
||||
}
|
||||
}
|
||||
case let .starGiftUnique(gift, isUpgrade, _, _, _, _, isRefunded, _, _, _, _, _, _, _, _, _, fromOffer, _):
|
||||
case let .starGiftUnique(gift, isUpgrade, _, _, _, _, isRefunded, _, _, _, _, _, _, _, _, _, fromOffer, _, _):
|
||||
if case let .unique(uniqueGift) = gift {
|
||||
isStarGift = true
|
||||
|
||||
|
|
|
|||
|
|
@ -4530,15 +4530,29 @@ public class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDeleg
|
|||
}
|
||||
}
|
||||
|
||||
public func chatInputTextNodeShouldReturn() -> Bool {
|
||||
public func chatInputTextNodeShouldReturn(modifierFlags: UIKeyModifierFlags) -> Bool {
|
||||
var shouldSendMessage = false
|
||||
if self.sendActionButtons.sendButton.supernode != nil && !self.sendActionButtons.sendButton.isHidden && !self.sendActionButtons.sendContainerNode.alpha.isZero {
|
||||
self.sendButtonPressed()
|
||||
if let context = self.context, context.sharedContext.currentChatSettings.with({ $0 }).sendWithCmdEnter {
|
||||
if modifierFlags.contains(.command) {
|
||||
shouldSendMessage = true
|
||||
}
|
||||
} else {
|
||||
if modifierFlags.isEmpty {
|
||||
shouldSendMessage = true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
if shouldSendMessage {
|
||||
self.sendButtonPressed()
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
@objc public func editableTextNodeShouldReturn(_ editableTextNode: ASEditableTextNode) -> Bool {
|
||||
return self.chatInputTextNodeShouldReturn()
|
||||
return self.chatInputTextNodeShouldReturn(modifierFlags: [])
|
||||
}
|
||||
|
||||
private func applyUpdateSendButtonIcon() {
|
||||
|
|
|
|||
|
|
@ -18,6 +18,7 @@ import UIKitRuntimeUtils
|
|||
public final class GiftCompositionComponent: Component {
|
||||
public class ExternalState {
|
||||
public fileprivate(set) var previewPatternColor: UIColor?
|
||||
public fileprivate(set) var backgroundColor: UIColor?
|
||||
public fileprivate(set) var previewModel: StarGift.UniqueGift.Attribute?
|
||||
public fileprivate(set) var previewBackdrop: StarGift.UniqueGift.Attribute?
|
||||
public fileprivate(set) var previewSymbol: StarGift.UniqueGift.Attribute?
|
||||
|
|
@ -766,6 +767,7 @@ public final class GiftCompositionComponent: Component {
|
|||
}
|
||||
}
|
||||
|
||||
component.externalState?.backgroundColor = backgroundColor
|
||||
component.externalState?.previewPatternColor = secondBackgroundColor
|
||||
|
||||
var animateBackdropSwipe = false
|
||||
|
|
|
|||
|
|
@ -49,6 +49,7 @@ swift_library(
|
|||
"//submodules/TelegramUI/Components/SpaceWarpView",
|
||||
"//submodules/ConfettiEffect",
|
||||
"//submodules/TelegramNotices",
|
||||
"//submodules/TelegramUI/Components/Gifts/GiftLoadingShimmerView",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -108,7 +108,7 @@ final class CraftTableComponent: Component {
|
|||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func setupFailAnimation() {
|
||||
func setupFailureAnimation() {
|
||||
guard !self.didSetupFinishAnimation else {
|
||||
return
|
||||
}
|
||||
|
|
@ -127,41 +127,29 @@ final class CraftTableComponent: Component {
|
|||
}
|
||||
for i in 0 ..< min(2, availableStickers.count) {
|
||||
if let sticker = availableStickers[i].view {
|
||||
self.animationView.setSticker(sticker, face: 3 - i, mirror: isUpsideDown)
|
||||
let face: Int
|
||||
if isUpsideDown {
|
||||
face = i + 1
|
||||
} else {
|
||||
face = 3 - i
|
||||
}
|
||||
self.animationView.setSticker(sticker, face: face, mirror: isUpsideDown)
|
||||
}
|
||||
}
|
||||
|
||||
self.state?.updated()
|
||||
self.flipFaces = isUpsideDown
|
||||
|
||||
Queue.mainQueue().after(0.3, {
|
||||
self.failWillFinish = true
|
||||
self.component?.willFinish(false)
|
||||
})
|
||||
|
||||
if let failOverlayView = self.failOverlay.view as? LottieComponent.View {
|
||||
failOverlayView.isHidden = false
|
||||
failOverlayView.onFrameUpdate = { [weak self] frameIndex in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if frameIndex >= 5 && !self.failDidStartCrossAnimation {
|
||||
self.failDidStartCrossAnimation = true
|
||||
self.craftFailPlayOnce.invoke(Void())
|
||||
}
|
||||
if frameIndex >= 65 && !self.failDidBringToFront {
|
||||
self.failDidBringToFront = true
|
||||
failOverlayView.superview?.bringSubviewToFront(failOverlayView)
|
||||
|
||||
self.animationView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.15, removeOnCompletion: false)
|
||||
}
|
||||
if frameIndex >= 75 && !self.failWillFinish {
|
||||
self.failWillFinish = true
|
||||
self.component?.willFinish(false)
|
||||
}
|
||||
if frameIndex >= 82 && !self.failDidFinish {
|
||||
self.failDidFinish = true
|
||||
|
||||
failOverlayView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||
self.component?.finished(nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
self.craftFailOverlayPlayOnce.invoke(Void())
|
||||
Queue.mainQueue().after(0.5, {
|
||||
self.failDidFinish = true
|
||||
self.component?.finished(nil)
|
||||
})
|
||||
|
||||
self.state?.updated(transition: .easeInOut(duration: 0.4))
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -171,6 +159,8 @@ final class CraftTableComponent: Component {
|
|||
}
|
||||
self.didSetupFinishAnimation = true
|
||||
|
||||
self.animationView.isSuccess = true
|
||||
|
||||
self.animationView.onFinishApproach = { [weak self] isUpsideDown in
|
||||
guard let self else {
|
||||
return
|
||||
|
|
@ -270,10 +260,10 @@ final class CraftTableComponent: Component {
|
|||
if index == 0 {
|
||||
faceItems.append(
|
||||
AnyComponentWithIdentity(id: "background", component: AnyComponent(
|
||||
RoundedRectangle(color: component.buttonColor, cornerRadius: 28.0)
|
||||
FilledRoundedRectangleComponent(color: component.buttonColor, cornerRadius: .value(28.0), smoothCorners: true)
|
||||
))
|
||||
)
|
||||
if !component.isCrafting {
|
||||
if !component.isCrafting || self.isFailed {
|
||||
faceItems.append(
|
||||
AnyComponentWithIdentity(id: "glass", component: AnyComponent(
|
||||
GlassBackgroundComponent(size: CGSize(width: cubeSide, height: cubeSide), cornerRadius: 28.0, isDark: true, tintColor: .init(kind: .custom(style: .default, color: component.buttonColor)))
|
||||
|
|
@ -282,11 +272,20 @@ final class CraftTableComponent: Component {
|
|||
}
|
||||
if self.isFailed {
|
||||
faceItems.append(
|
||||
AnyComponentWithIdentity(id: "fail", component: AnyComponent(
|
||||
LottieComponent(
|
||||
content: LottieComponent.AppBundleContent(name: "CraftFail"),
|
||||
size: CGSize(width: 96.0, height: 96.0),
|
||||
playOnce: self.craftFailPlayOnce
|
||||
AnyComponentWithIdentity(id: "faildial", component: AnyComponent(
|
||||
DialIndicatorComponent(
|
||||
content: AnyComponentWithIdentity(id: "gift", component: AnyComponent(BundleIconComponent(name: "Premium/GiftCrash", tintColor: .white))),
|
||||
backgroundColor: .white.withAlphaComponent(0.1),
|
||||
foregroundColor: .white,
|
||||
diameter: 84.0,
|
||||
contentSize: CGSize(width: 44.0, height: 44.0),
|
||||
lineWidth: 5.0,
|
||||
fontSize: 18.0,
|
||||
progress: 0.0,
|
||||
value: component.gifts.count,
|
||||
suffix: "",
|
||||
isVisible: true,
|
||||
isFlipped: self.flipFaces
|
||||
)
|
||||
))
|
||||
)
|
||||
|
|
@ -300,7 +299,9 @@ final class CraftTableComponent: Component {
|
|||
diameter: 84.0,
|
||||
lineWidth: 5.0,
|
||||
fontSize: 18.0,
|
||||
percentage: permilleValue / 10,
|
||||
progress: CGFloat(permilleValue / 10 / 100),
|
||||
value: permilleValue / 10,
|
||||
suffix: "%",
|
||||
isVisible: !component.isCrafting
|
||||
)
|
||||
))
|
||||
|
|
@ -318,7 +319,7 @@ final class CraftTableComponent: Component {
|
|||
} else {
|
||||
faceItems.append(
|
||||
AnyComponentWithIdentity(id: "background", component: AnyComponent(
|
||||
RoundedRectangle(color: component.buttonColor, cornerRadius: 28.0)
|
||||
FilledRoundedRectangleComponent(color: component.buttonColor, cornerRadius: .value(28.0), smoothCorners: true)
|
||||
))
|
||||
)
|
||||
faceItems.append(
|
||||
|
|
@ -412,35 +413,36 @@ final class CraftTableComponent: Component {
|
|||
self.setupSuccessAnimation(uniqueGift)
|
||||
}
|
||||
case .fail:
|
||||
self.setupFailAnimation()
|
||||
self.setupFailureAnimation()
|
||||
default:
|
||||
break
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
if self.isFailed {
|
||||
let failOverlaySize = self.failOverlay.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
LottieComponent(
|
||||
content: LottieComponent.AppBundleContent(name: "CraftFailOverlay"),
|
||||
size: CGSize(width: availableSize.width, height: availableSize.width),
|
||||
playOnce: self.craftFailOverlayPlayOnce
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width, height: availableSize.width)
|
||||
)
|
||||
let failOverlayFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - failOverlaySize.width) / 2.0), y: floor((availableSize.height - failOverlaySize.height) / 2.0)), size: failOverlaySize)
|
||||
if let failOverlayView = self.failOverlay.view {
|
||||
if failOverlayView.superview == nil {
|
||||
failOverlayView.isHidden = true
|
||||
self.insertSubview(failOverlayView, belowSubview: self.animationView)
|
||||
}
|
||||
failOverlayView.frame = failOverlayFrame
|
||||
}
|
||||
}
|
||||
// if self.isFailed {
|
||||
// let failOverlaySize = self.failOverlay.update(
|
||||
// transition: .immediate,
|
||||
// component: AnyComponent(
|
||||
// LottieComponent(
|
||||
// content: LottieComponent.AppBundleContent(name: "CraftFailOverlay"),
|
||||
// size: CGSize(width: availableSize.width, height: availableSize.width),
|
||||
// playOnce: self.craftFailOverlayPlayOnce
|
||||
// )
|
||||
// ),
|
||||
// environment: {},
|
||||
// containerSize: CGSize(width: availableSize.width, height: availableSize.width)
|
||||
// )
|
||||
// let failOverlayFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - failOverlaySize.width) / 2.0), y: floor((availableSize.height - failOverlaySize.height) / 2.0)), size: failOverlaySize)
|
||||
// if let failOverlayView = self.failOverlay.view {
|
||||
// if failOverlayView.superview == nil {
|
||||
// failOverlayView.isHidden = true
|
||||
// self.insertSubview(failOverlayView, belowSubview: self.animationView)
|
||||
// }
|
||||
// failOverlayView.frame = failOverlayFrame
|
||||
// }
|
||||
// }
|
||||
|
||||
return availableSize
|
||||
}
|
||||
|
|
@ -544,9 +546,9 @@ final class GiftSlotComponent: Component {
|
|||
self.state = state
|
||||
|
||||
let backgroundFrame = CGRect(origin: .zero, size: availableSize).insetBy(dx: 1.0, dy: 1.0)
|
||||
self.backgroundView.update(size: backgroundFrame.size, cornerRadius: 28.0, isDark: true, tintColor: .init(kind: .custom(style: .default, color: component.buttonColor)), transition: .immediate)
|
||||
self.backgroundView.update(size: backgroundFrame.size, cornerRadius: 28.0, isDark: true, tintColor: .init(kind: .custom(style: .default, color: component.buttonColor)), isInteractive: true, transition: .immediate)
|
||||
transition.setFrame(view: self.backgroundView, frame: backgroundFrame)
|
||||
if component.gift == nil && component.isCrafting {
|
||||
if component.gift == nil && component.isCrafting && previousComponent?.isCrafting == false {
|
||||
transition.setBlur(layer: self.backgroundView.layer, radius: 10.0)
|
||||
self.backgroundView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.35, removeOnCompletion: false)
|
||||
transition.setBlur(layer: self.addIcon.layer, radius: 10.0)
|
||||
|
|
|
|||
|
|
@ -79,6 +79,8 @@ final class CubeAnimationView: UIView {
|
|||
private var finishDelayTimerY: Timer?
|
||||
private var cubeScale: Float = 1.0
|
||||
private var hasFiredFinishApproach = false
|
||||
|
||||
var isSuccess = false
|
||||
|
||||
var onFinishApproach: ((Bool) -> Void)?
|
||||
|
||||
|
|
@ -485,7 +487,7 @@ final class CubeAnimationView: UIView {
|
|||
let duration: TimeInterval = 0.2
|
||||
let startQuad = Quad(rect: sticker.frame)
|
||||
let animationView: UIView
|
||||
if let snapshot = sticker.snapshotView(afterScreenUpdates: true) {
|
||||
if let snapshot = sticker.snapshotView(afterScreenUpdates: false) {
|
||||
self.warpSnapshot?.removeFromSuperview()
|
||||
self.warpSnapshot = snapshot
|
||||
|
||||
|
|
@ -606,7 +608,7 @@ final class CubeAnimationView: UIView {
|
|||
}
|
||||
let stickerIndex = indices[index]
|
||||
if self.stickers.indices.contains(stickerIndex) {
|
||||
let isLast = stickerIndex == indices.count - 1
|
||||
let isLast = index == indices.count - 1
|
||||
self.launchStickerView(self.stickers[stickerIndex], emphasized: isLast, willFinish: isLast)
|
||||
}
|
||||
self.scheduleStickerSequence(from: index + 1, indices: indices)
|
||||
|
|
@ -697,10 +699,12 @@ final class CubeAnimationView: UIView {
|
|||
let upsideDown = abs(shortestAngleDelta(from: self.rotation.x, to: Float.pi)) < (Float.pi / 2)
|
||||
self.onFinishApproach?(upsideDown)
|
||||
}
|
||||
if absRemaining <= self.finishSuccessScaleTriggerAngle {
|
||||
if self.isSuccess, absRemaining <= self.finishSuccessScaleTriggerAngle {
|
||||
let raw = (self.finishSuccessScaleTriggerAngle - absRemaining) / self.finishSuccessScaleTriggerAngle
|
||||
let eased = raw * raw * (3 - 2 * raw)
|
||||
self.cubeScale = 1.0 + (self.finishSuccessScale - 1.0) * eased
|
||||
} else if !self.isSuccess {
|
||||
self.cubeScale = 1.0
|
||||
}
|
||||
if abs(remaining) < 0.0008 && abs(self.angularVelocity.y) < 0.0015 {
|
||||
self.finishRotationY = self.finishTargetYUnwrapped
|
||||
|
|
|
|||
|
|
@ -13,29 +13,41 @@ final class DialIndicatorComponent: Component {
|
|||
let backgroundColor: UIColor
|
||||
let foregroundColor: UIColor
|
||||
let diameter: CGFloat
|
||||
let contentSize: CGSize?
|
||||
let lineWidth: CGFloat
|
||||
let fontSize: CGFloat
|
||||
let percentage: Int
|
||||
let progress: CGFloat
|
||||
let value: Int
|
||||
let suffix: String
|
||||
let isVisible: Bool
|
||||
let isFlipped: Bool
|
||||
|
||||
public init(
|
||||
content: AnyComponentWithIdentity<Empty>,
|
||||
backgroundColor: UIColor,
|
||||
foregroundColor: UIColor,
|
||||
diameter: CGFloat,
|
||||
contentSize: CGSize? = nil,
|
||||
lineWidth: CGFloat,
|
||||
fontSize: CGFloat,
|
||||
percentage: Int,
|
||||
isVisible: Bool = true
|
||||
progress: CGFloat,
|
||||
value: Int,
|
||||
suffix: String,
|
||||
isVisible: Bool = true,
|
||||
isFlipped: Bool = false,
|
||||
) {
|
||||
self.content = content
|
||||
self.backgroundColor = backgroundColor
|
||||
self.foregroundColor = foregroundColor
|
||||
self.diameter = diameter
|
||||
self.contentSize = contentSize
|
||||
self.lineWidth = lineWidth
|
||||
self.fontSize = fontSize
|
||||
self.percentage = percentage
|
||||
self.progress = progress
|
||||
self.value = value
|
||||
self.suffix = suffix
|
||||
self.isVisible = isVisible
|
||||
self.isFlipped = isFlipped
|
||||
}
|
||||
|
||||
public static func ==(lhs: DialIndicatorComponent, rhs: DialIndicatorComponent) -> Bool {
|
||||
|
|
@ -51,18 +63,30 @@ final class DialIndicatorComponent: Component {
|
|||
if lhs.diameter != rhs.diameter {
|
||||
return false
|
||||
}
|
||||
if lhs.contentSize != rhs.contentSize {
|
||||
return false
|
||||
}
|
||||
if lhs.lineWidth != rhs.lineWidth {
|
||||
return false
|
||||
}
|
||||
if lhs.fontSize != rhs.fontSize {
|
||||
return false
|
||||
}
|
||||
if lhs.percentage != rhs.percentage {
|
||||
if lhs.progress != rhs.progress {
|
||||
return false
|
||||
}
|
||||
if lhs.value != rhs.value {
|
||||
return false
|
||||
}
|
||||
if lhs.suffix != rhs.suffix {
|
||||
return false
|
||||
}
|
||||
if lhs.isVisible != rhs.isVisible {
|
||||
return false
|
||||
}
|
||||
if lhs.isFlipped != rhs.isFlipped {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
|
|
@ -82,7 +106,7 @@ final class DialIndicatorComponent: Component {
|
|||
|
||||
self.backgroundLayer.lineCap = .round
|
||||
self.foregroundLayer.lineCap = .round
|
||||
|
||||
|
||||
self.addSubview(self.containerView)
|
||||
|
||||
self.containerView.layer.addSublayer(self.backgroundLayer)
|
||||
|
|
@ -119,7 +143,7 @@ final class DialIndicatorComponent: Component {
|
|||
self.foregroundLayer.path = CGPath(ellipseIn: pathFrame, transform: nil)
|
||||
self.foregroundLayer.transform = CATransform3DMakeRotation(.pi / 2.0, 0.0, 0.0, 1.0)
|
||||
self.foregroundLayer.strokeStart = strokeStart
|
||||
transition.setShapeLayerStrokeEnd(layer: self.foregroundLayer, strokeEnd: strokeStart + (strokeEnd - strokeStart) * (CGFloat(component.percentage) / 100.0))
|
||||
transition.setShapeLayerStrokeEnd(layer: self.foregroundLayer, strokeEnd: strokeStart + (strokeEnd - strokeStart) * component.progress)
|
||||
self.foregroundLayer.frame = CGRect(origin: .zero, size: pathSize)
|
||||
|
||||
if previousComponent?.content.id != component.content.id {
|
||||
|
|
@ -136,7 +160,8 @@ final class DialIndicatorComponent: Component {
|
|||
self.content = ComponentView()
|
||||
}
|
||||
|
||||
let contentFrame = CGRect(origin: CGPoint(x: 8.0, y: 8.0), size: CGSize(width: component.diameter - 16.0, height: component.diameter - 16.0))
|
||||
let contentSize = component.contentSize ?? CGSize(width: component.diameter - 16.0, height: component.diameter - 16.0)
|
||||
let contentFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((pathSize.width - contentSize.width) / 2.0), y: floorToScreenPixels((pathSize.height - contentSize.height) / 2.0)), size: contentSize)
|
||||
let _ = self.content.update(
|
||||
transition: .immediate,
|
||||
component: component.content.component,
|
||||
|
|
@ -154,10 +179,12 @@ final class DialIndicatorComponent: Component {
|
|||
contentView.frame = contentFrame
|
||||
}
|
||||
|
||||
let labelItems: [AnimatedTextComponent.Item] = [
|
||||
AnimatedTextComponent.Item(id: "percent", content: .number(component.percentage, minDigits: 1)),
|
||||
AnimatedTextComponent.Item(id: "suffix", content: .text("%"))
|
||||
var labelItems: [AnimatedTextComponent.Item] = [
|
||||
AnimatedTextComponent.Item(id: "percent", content: .number(component.value, minDigits: 1))
|
||||
]
|
||||
if !component.suffix.isEmpty {
|
||||
labelItems.append(AnimatedTextComponent.Item(id: "suffix", content: .text(component.suffix)))
|
||||
}
|
||||
|
||||
let labelSize = self.label.update(
|
||||
transition: transition,
|
||||
|
|
@ -181,6 +208,8 @@ final class DialIndicatorComponent: Component {
|
|||
transition.setAlpha(view: self.containerView, alpha: component.isVisible ? 1.0 : 0.0)
|
||||
transition.setBlur(layer: self.containerView.layer, radius: component.isVisible ? 0.0 : 10.0)
|
||||
|
||||
self.containerView.transform = CGAffineTransform(rotationAngle: component.isFlipped ? .pi : 0.0)
|
||||
|
||||
self.containerView.frame = CGRect(origin: .zero, size: pathSize)
|
||||
|
||||
return pathSize
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -21,6 +21,7 @@ import ResizableSheetComponent
|
|||
import TooltipUI
|
||||
import GlassBarButtonComponent
|
||||
import ConfettiEffect
|
||||
import GiftLoadingShimmerView
|
||||
|
||||
final class SelectGiftPageContent: Component {
|
||||
typealias EnvironmentType = ViewControllerComponentContainer.Environment
|
||||
|
|
@ -34,6 +35,7 @@ final class SelectGiftPageContent: Component {
|
|||
let starsTopUpOptions: Signal<[StarsTopUpOption]?, NoError>
|
||||
let selectGift: (GiftItem) -> Void
|
||||
let dismiss: () -> Void
|
||||
let boundsUpdated: ActionSlot<CGRect>
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
|
|
@ -44,7 +46,8 @@ final class SelectGiftPageContent: Component {
|
|||
selectedGiftIds: Set<Int64>,
|
||||
starsTopUpOptions: Signal<[StarsTopUpOption]?, NoError>,
|
||||
selectGift: @escaping (GiftItem) -> Void,
|
||||
dismiss: @escaping () -> Void
|
||||
dismiss: @escaping () -> Void,
|
||||
boundsUpdated: ActionSlot<CGRect>
|
||||
) {
|
||||
self.context = context
|
||||
self.craftContext = craftContext
|
||||
|
|
@ -55,6 +58,7 @@ final class SelectGiftPageContent: Component {
|
|||
self.starsTopUpOptions = starsTopUpOptions
|
||||
self.selectGift = selectGift
|
||||
self.dismiss = dismiss
|
||||
self.boundsUpdated = boundsUpdated
|
||||
}
|
||||
|
||||
static func ==(lhs: SelectGiftPageContent, rhs: SelectGiftPageContent) -> Bool {
|
||||
|
|
@ -74,6 +78,7 @@ final class SelectGiftPageContent: Component {
|
|||
private let myGiftsTitle = ComponentView<Empty>()
|
||||
private var gifts: [AnyHashable: ComponentView<Empty>] = [:]
|
||||
private let myGiftsPlaceholder = ComponentView<Empty>()
|
||||
private let loadingView = GiftLoadingShimmerView()
|
||||
|
||||
private let storeGiftsTitle = ComponentView<Empty>()
|
||||
private let storeGifts = ComponentView<Empty>()
|
||||
|
|
@ -84,6 +89,9 @@ final class SelectGiftPageContent: Component {
|
|||
private var availableGifts: [GiftItem] = []
|
||||
private var giftMap: [Int64: GiftItem] = [:]
|
||||
|
||||
private var availableSize: CGSize?
|
||||
private var currentBounds: CGRect?
|
||||
|
||||
private var component: SelectGiftPageContent?
|
||||
private weak var state: EmptyComponentState?
|
||||
private var environment: ViewControllerComponentContainer.Environment?
|
||||
|
|
@ -94,6 +102,8 @@ final class SelectGiftPageContent: Component {
|
|||
|
||||
self.layer.cornerRadius = 40.0
|
||||
self.layer.maskedCorners = [.layerMinXMinYCorner, .layerMaxXMinYCorner]
|
||||
|
||||
self.addSubview(self.loadingView)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
|
|
@ -103,6 +113,187 @@ final class SelectGiftPageContent: Component {
|
|||
deinit {
|
||||
self.craftStateDisposable?.dispose()
|
||||
}
|
||||
|
||||
func updateScrolling(interactive: Bool, transition: ComponentTransition) -> CGFloat {
|
||||
guard let bounds = self.currentBounds, let availableSize = self.availableSize, let component = self.component, let environment = self.environment else {
|
||||
return 0.0
|
||||
}
|
||||
|
||||
let visibleBounds = bounds.insetBy(dx: 0.0, dy: -10.0)
|
||||
|
||||
var contentHeight: CGFloat = 88.0 + 32.0
|
||||
|
||||
let itemSpacing: CGFloat = 10.0
|
||||
let itemSideInset = 16.0
|
||||
let itemsInRow: Int
|
||||
if availableSize.width > availableSize.height || availableSize.width > 480.0 {
|
||||
if case .tablet = environment.deviceMetrics.type {
|
||||
itemsInRow = 4
|
||||
} else {
|
||||
itemsInRow = 5
|
||||
}
|
||||
} else {
|
||||
itemsInRow = 3
|
||||
}
|
||||
let itemWidth = (availableSize.width - itemSideInset * 2.0 - itemSpacing * CGFloat(itemsInRow - 1)) / CGFloat(itemsInRow)
|
||||
let itemSize = CGSize(width: itemWidth, height: itemWidth)
|
||||
|
||||
var isLoading = false
|
||||
if self.availableGifts.isEmpty, case .loading = (self.craftState?.dataState ?? .loading) {
|
||||
isLoading = true
|
||||
}
|
||||
let loadingTransition: ComponentTransition = .easeInOut(duration: 0.25)
|
||||
let loadingSize = CGSize(width: availableSize.width, height: 180.0)
|
||||
if isLoading {
|
||||
contentHeight += 120.0
|
||||
self.loadingView.update(size: loadingSize, theme: environment.theme, itemSize: itemSize, showFilters: false, isPlain: true, transition: .immediate)
|
||||
loadingTransition.setAlpha(view: self.loadingView, alpha: 1.0)
|
||||
} else {
|
||||
loadingTransition.setAlpha(view: self.loadingView, alpha: 0.0)
|
||||
}
|
||||
transition.setFrame(view: self.loadingView, frame: CGRect(origin: CGPoint(x: 0.0, y: contentHeight - 170.0), size: loadingSize))
|
||||
|
||||
var itemFrame = CGRect(origin: CGPoint(x: itemSideInset, y: contentHeight), size: itemSize)
|
||||
var itemsHeight: CGFloat = 0.0
|
||||
var validIds: [AnyHashable] = []
|
||||
for gift in self.availableGifts {
|
||||
var isVisible = false
|
||||
if visibleBounds.intersects(itemFrame) {
|
||||
isVisible = true
|
||||
}
|
||||
if isVisible {
|
||||
let itemId = AnyHashable(gift.gift.id)
|
||||
validIds.append(itemId)
|
||||
|
||||
var itemTransition = transition
|
||||
let visibleItem: ComponentView<Empty>
|
||||
if let current = self.gifts[itemId] {
|
||||
visibleItem = current
|
||||
} else {
|
||||
visibleItem = ComponentView()
|
||||
self.gifts[itemId] = visibleItem
|
||||
itemTransition = .immediate
|
||||
}
|
||||
|
||||
var ribbonColor: GiftItemComponent.Ribbon.Color = .blue
|
||||
let ribbonText = "#\(gift.gift.number)"
|
||||
for attribute in gift.gift.attributes {
|
||||
if case let .backdrop(_, _, innerColor, outerColor, _, _, _) = attribute {
|
||||
ribbonColor = .custom(outerColor, innerColor)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
let _ = visibleItem.update(
|
||||
transition: itemTransition,
|
||||
component: AnyComponent(
|
||||
GiftItemComponent(
|
||||
context: component.context,
|
||||
style: .glass,
|
||||
theme: environment.theme,
|
||||
strings: environment.strings,
|
||||
peer: nil,
|
||||
subject: .uniqueGift(gift: gift.gift, price: nil),
|
||||
ribbon: GiftItemComponent.Ribbon(text: ribbonText, font: .monospaced, color: ribbonColor, outline: nil),
|
||||
badge: gift.gift.craftChancePermille.flatMap { "+\($0 / 10)%" },
|
||||
resellPrice: nil,
|
||||
isHidden: false,
|
||||
isSelected: false,
|
||||
isPinned: false,
|
||||
isEditing: false,
|
||||
mode: .grid,
|
||||
action: { [weak self] in
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
component.selectGift(gift)
|
||||
component.dismiss()
|
||||
},
|
||||
contextAction: { _, _ in }
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: itemSize
|
||||
)
|
||||
if let itemView = visibleItem.view {
|
||||
if itemView.superview == nil {
|
||||
if let _ = self.loadingView.superview {
|
||||
self.insertSubview(itemView, belowSubview: self.loadingView)
|
||||
} else {
|
||||
self.addSubview(itemView)
|
||||
}
|
||||
if !transition.animation.isImmediate {
|
||||
itemView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25)
|
||||
}
|
||||
}
|
||||
itemTransition.setFrame(view: itemView, frame: itemFrame)
|
||||
}
|
||||
}
|
||||
|
||||
itemsHeight = itemFrame.maxY - contentHeight
|
||||
|
||||
itemFrame.origin.x += itemFrame.width + itemSpacing
|
||||
if itemFrame.maxX > availableSize.width {
|
||||
itemFrame.origin.x = itemSideInset
|
||||
itemFrame.origin.y += itemSize.height + itemSpacing
|
||||
}
|
||||
}
|
||||
|
||||
var removeIds: [AnyHashable] = []
|
||||
for (id, item) in self.gifts {
|
||||
if !validIds.contains(id) {
|
||||
removeIds.append(id)
|
||||
if let itemView = item.view {
|
||||
if !transition.animation.isImmediate {
|
||||
itemView.layer.animateScale(from: 1.0, to: 0.01, duration: 0.25, removeOnCompletion: false)
|
||||
itemView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in
|
||||
itemView.removeFromSuperview()
|
||||
})
|
||||
} else {
|
||||
itemView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for id in removeIds {
|
||||
self.gifts.removeValue(forKey: id)
|
||||
}
|
||||
|
||||
if let state = self.craftState, case .ready = state.dataState, self.availableGifts.isEmpty {
|
||||
contentHeight += 10.0
|
||||
let myGiftsPlaceholderSize = self.myGiftsPlaceholder.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: environment.strings.Gift_Craft_Select_NoGiftsFromCollection, font: Font.regular(13.0), textColor: environment.theme.list.itemSecondaryTextColor)),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 3,
|
||||
lineSpacing: 0.1
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - 32.0, height: .greatestFiniteMagnitude)
|
||||
)
|
||||
let myGiftsPlaceholderFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - myGiftsPlaceholderSize.width) / 2.0), y: contentHeight), size: myGiftsPlaceholderSize)
|
||||
if let myGiftsPlaceholderView = self.myGiftsPlaceholder.view {
|
||||
if myGiftsPlaceholderView.superview == nil {
|
||||
self.addSubview(myGiftsPlaceholderView)
|
||||
}
|
||||
myGiftsPlaceholderView.frame = myGiftsPlaceholderFrame
|
||||
}
|
||||
contentHeight += myGiftsPlaceholderSize.height
|
||||
contentHeight += 32.0
|
||||
} else {
|
||||
contentHeight += itemsHeight
|
||||
contentHeight += 24.0
|
||||
}
|
||||
|
||||
if let storeGiftsView = self.storeGifts.view as? GiftStoreContentComponent.View {
|
||||
storeGiftsView.updateScrolling(bounds: bounds.offsetBy(dx: 0.0, dy: -contentHeight), interactive: interactive, transition: .immediate)
|
||||
}
|
||||
|
||||
return contentHeight
|
||||
}
|
||||
|
||||
func update(component: SelectGiftPageContent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<ViewControllerComponentContainer.Environment>, transition: ComponentTransition) -> CGSize {
|
||||
self.isUpdating = true
|
||||
|
|
@ -110,7 +301,18 @@ final class SelectGiftPageContent: Component {
|
|||
self.isUpdating = false
|
||||
}
|
||||
|
||||
self.availableSize = availableSize
|
||||
if self.component == nil {
|
||||
self.currentBounds = CGRect(origin: .zero, size: availableSize)
|
||||
|
||||
component.boundsUpdated.connect { [weak self] bounds in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.currentBounds = bounds
|
||||
let _ = self.updateScrolling(interactive: true, transition: .immediate)
|
||||
}
|
||||
|
||||
let initialGiftItem = GiftItem(
|
||||
gift: component.gift,
|
||||
reference: .slug(slug: component.gift.slug)
|
||||
|
|
@ -125,7 +327,6 @@ final class SelectGiftPageContent: Component {
|
|||
guard let self else {
|
||||
return
|
||||
}
|
||||
//let isFirstTime = self.craftState == nil
|
||||
self.craftState = state
|
||||
|
||||
var items: [GiftItem] = []
|
||||
|
|
@ -175,7 +376,7 @@ final class SelectGiftPageContent: Component {
|
|||
let myGiftsTitleSize = self.myGiftsTitle.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: "Your Gifts".uppercased(), font: Font.semibold(14.0), textColor: environment.theme.actionSheet.secondaryTextColor)))
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: environment.strings.Gift_Craft_Select_YourGifts.uppercased(), font: Font.semibold(14.0), textColor: environment.theme.actionSheet.secondaryTextColor)))
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 100.0)
|
||||
|
|
@ -190,165 +391,12 @@ final class SelectGiftPageContent: Component {
|
|||
|
||||
contentHeight += 32.0
|
||||
|
||||
let itemSpacing: CGFloat = 10.0
|
||||
let itemSideInset = 16.0
|
||||
let itemsInRow: Int
|
||||
if availableSize.width > availableSize.height || availableSize.width > 480.0 {
|
||||
if case .tablet = environment.deviceMetrics.type {
|
||||
itemsInRow = 4
|
||||
} else {
|
||||
itemsInRow = 5
|
||||
}
|
||||
} else {
|
||||
itemsInRow = 3
|
||||
}
|
||||
let itemWidth = (availableSize.width - itemSideInset * 2.0 - itemSpacing * CGFloat(itemsInRow - 1)) / CGFloat(itemsInRow)
|
||||
let itemSize = CGSize(width: itemWidth, height: itemWidth)
|
||||
var itemFrame = CGRect(origin: CGPoint(x: itemSideInset, y: contentHeight), size: itemSize)
|
||||
|
||||
var itemsHeight: CGFloat = 0.0
|
||||
|
||||
var validIds: [AnyHashable] = []
|
||||
for gift in self.availableGifts {
|
||||
let isVisible = "".isEmpty
|
||||
// if visibleBounds.intersects(itemFrame) {
|
||||
// isVisible = true
|
||||
// }
|
||||
if isVisible {
|
||||
let itemId = AnyHashable(gift.gift.id)
|
||||
validIds.append(itemId)
|
||||
|
||||
var itemTransition = transition
|
||||
let visibleItem: ComponentView<Empty>
|
||||
if let current = self.gifts[itemId] {
|
||||
visibleItem = current
|
||||
} else {
|
||||
visibleItem = ComponentView()
|
||||
self.gifts[itemId] = visibleItem
|
||||
itemTransition = .immediate
|
||||
}
|
||||
|
||||
var ribbonColor: GiftItemComponent.Ribbon.Color = .blue
|
||||
let ribbonText = "#\(gift.gift.number)"
|
||||
for attribute in gift.gift.attributes {
|
||||
if case let .backdrop(_, _, innerColor, outerColor, _, _, _) = attribute {
|
||||
ribbonColor = .custom(outerColor, innerColor)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
let badge: String? = gift.gift.craftChancePermille.flatMap { "+\($0 / 10)%" }
|
||||
|
||||
let _ = visibleItem.update(
|
||||
transition: itemTransition,
|
||||
component: AnyComponent(
|
||||
GiftItemComponent(
|
||||
context: component.context,
|
||||
style: .glass,
|
||||
theme: environment.theme,
|
||||
strings: environment.strings,
|
||||
peer: nil,
|
||||
subject: .uniqueGift(gift: gift.gift, price: nil),
|
||||
ribbon: GiftItemComponent.Ribbon(text: ribbonText, font: .monospaced, color: ribbonColor, outline: nil),
|
||||
badge: badge,
|
||||
resellPrice: nil,
|
||||
isHidden: false,
|
||||
isSelected: false,
|
||||
isPinned: false,
|
||||
isEditing: false,
|
||||
mode: .grid,
|
||||
action: { [weak self] in
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
component.selectGift(gift)
|
||||
component.dismiss()
|
||||
},
|
||||
contextAction: { _, _ in }
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: itemSize
|
||||
)
|
||||
if let itemView = visibleItem.view {
|
||||
if itemView.superview == nil {
|
||||
self.addSubview(itemView)
|
||||
|
||||
if !transition.animation.isImmediate {
|
||||
let delay = ((itemFrame.minY - contentHeight) / itemSize.height) * 0.07
|
||||
itemView.layer.animateScale(from: 0.01, to: 1.0, duration: 0.25, delay: delay)
|
||||
itemView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25, delay: delay)
|
||||
}
|
||||
}
|
||||
itemView.isUserInteractionEnabled = !component.selectedGiftIds.contains(gift.gift.id)
|
||||
itemView.alpha = component.selectedGiftIds.contains(gift.gift.id) ? 0.4 : 1.0
|
||||
itemView.layer.allowsGroupOpacity = itemView.alpha < 1.0
|
||||
itemTransition.setFrame(view: itemView, frame: itemFrame)
|
||||
}
|
||||
}
|
||||
|
||||
itemsHeight = itemFrame.maxY - contentHeight
|
||||
|
||||
itemFrame.origin.x += itemFrame.width + itemSpacing
|
||||
if itemFrame.maxX > availableSize.width {
|
||||
itemFrame.origin.x = itemSideInset
|
||||
itemFrame.origin.y += itemSize.height + itemSpacing
|
||||
}
|
||||
}
|
||||
|
||||
var removeIds: [AnyHashable] = []
|
||||
for (id, item) in self.gifts {
|
||||
if !validIds.contains(id) {
|
||||
removeIds.append(id)
|
||||
if let itemView = item.view {
|
||||
if !transition.animation.isImmediate {
|
||||
itemView.layer.animateScale(from: 1.0, to: 0.01, duration: 0.25, removeOnCompletion: false)
|
||||
itemView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.25, removeOnCompletion: false, completion: { _ in
|
||||
itemView.removeFromSuperview()
|
||||
})
|
||||
} else {
|
||||
itemView.removeFromSuperview()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
for id in removeIds {
|
||||
self.gifts.removeValue(forKey: id)
|
||||
}
|
||||
|
||||
if let state = self.craftState, case .ready = state.dataState, self.availableGifts.isEmpty {
|
||||
contentHeight += 10.0
|
||||
let myGiftsPlaceholderSize = self.myGiftsPlaceholder.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(string: "You don't have other gifts\nfrom this collection", font: Font.regular(13.0), textColor: environment.theme.list.itemSecondaryTextColor)),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 3,
|
||||
lineSpacing: 0.1
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - 32.0, height: .greatestFiniteMagnitude)
|
||||
)
|
||||
let myGiftsPlaceholderFrame = CGRect(origin: CGPoint(x: floor((availableSize.width - myGiftsPlaceholderSize.width) / 2.0), y: contentHeight), size: myGiftsPlaceholderSize)
|
||||
if let myGiftsPlaceholderView = self.myGiftsPlaceholder.view {
|
||||
if myGiftsPlaceholderView.superview == nil {
|
||||
self.addSubview(myGiftsPlaceholderView)
|
||||
}
|
||||
myGiftsPlaceholderView.frame = myGiftsPlaceholderFrame
|
||||
}
|
||||
contentHeight += myGiftsPlaceholderSize.height
|
||||
contentHeight += 32.0
|
||||
} else {
|
||||
contentHeight += itemsHeight
|
||||
contentHeight += 24.0
|
||||
}
|
||||
contentHeight = self.updateScrolling(interactive: false, transition: transition)
|
||||
|
||||
let storeGiftsTitleSize = self.storeGiftsTitle.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: "SUITABLE GIFTS ON SALE".uppercased(), font: Font.semibold(14.0), textColor: environment.theme.actionSheet.secondaryTextColor)))
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: environment.strings.Gift_Craft_Select_SaleGifts.uppercased(), font: Font.semibold(14.0), textColor: environment.theme.actionSheet.secondaryTextColor)))
|
||||
),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 100.0)
|
||||
|
|
@ -379,6 +427,7 @@ final class SelectGiftPageContent: Component {
|
|||
starsContext: component.context.starsContext!,
|
||||
peerId: component.context.account.peerId,
|
||||
gift: component.genericGift,
|
||||
isPlain: true,
|
||||
confirmPurchaseImmediately: true,
|
||||
starsTopUpOptions: component.starsTopUpOptions,
|
||||
scrollToTop: {},
|
||||
|
|
@ -479,6 +528,8 @@ private final class SheetContainerComponent: CombinedComponent {
|
|||
static var body: Body {
|
||||
let sheet = Child(ResizableSheetComponent<EnvironmentType>.self)
|
||||
let animateOut = StoredActionSlot(Action<Void>.self)
|
||||
|
||||
let boundsUpdated = ActionSlot<CGRect>()
|
||||
|
||||
return { context in
|
||||
let component = context.component
|
||||
|
|
@ -518,11 +569,12 @@ private final class SheetContainerComponent: CombinedComponent {
|
|||
selectGift: component.selectGift,
|
||||
dismiss: {
|
||||
dismiss(true)
|
||||
}
|
||||
},
|
||||
boundsUpdated: boundsUpdated
|
||||
)
|
||||
),
|
||||
titleItem: AnyComponent(
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: "Select Gifts", font: Font.semibold(17.0), textColor: environment.theme.actionSheet.primaryTextColor)))
|
||||
MultilineTextComponent(text: .plain(NSAttributedString(string: environment.strings.Gift_Craft_Select_Title, font: Font.semibold(17.0), textColor: environment.theme.actionSheet.primaryTextColor)))
|
||||
),
|
||||
leftItem: AnyComponent(
|
||||
GlassBarButtonComponent(
|
||||
|
|
@ -530,9 +582,9 @@ private final class SheetContainerComponent: CombinedComponent {
|
|||
backgroundColor: nil,
|
||||
isDark: theme.overallDarkAppearance,
|
||||
state: .glass,
|
||||
component: AnyComponentWithIdentity(id: "back", component: AnyComponent(
|
||||
component: AnyComponentWithIdentity(id: "close", component: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Navigation/Back",
|
||||
name: "Navigation/Close",
|
||||
tintColor: theme.chat.inputPanel.panelControlColor
|
||||
)
|
||||
)),
|
||||
|
|
@ -561,7 +613,8 @@ private final class SheetContainerComponent: CombinedComponent {
|
|||
regularMetricsSize: CGSize(width: 430.0, height: 900.0),
|
||||
dismiss: { animated in
|
||||
dismiss(animated)
|
||||
}
|
||||
},
|
||||
boundsUpdated: boundsUpdated
|
||||
)
|
||||
},
|
||||
availableSize: context.availableSize,
|
||||
|
|
|
|||
|
|
@ -792,9 +792,7 @@ public final class GiftItemComponent: Component {
|
|||
func formatPercentage(_ value: Float) -> String {
|
||||
return String(format: "%0.1f", value).replacingOccurrences(of: ".0", with: "").replacingOccurrences(of: ",0", with: "") + "%"
|
||||
}
|
||||
|
||||
//TODO:localize
|
||||
|
||||
|
||||
let badgeString: String
|
||||
var badgeColor: UIColor?
|
||||
switch rarity {
|
||||
|
|
@ -805,21 +803,28 @@ public final class GiftItemComponent: Component {
|
|||
badgeString = formatPercentage(Float(value) * 0.1)
|
||||
}
|
||||
case .epic:
|
||||
badgeString = "epic"
|
||||
badgeString = component.strings.Gift_Attribute_Epic
|
||||
badgeColor = UIColor(rgb: 0xaf52de)
|
||||
case .legendary:
|
||||
badgeString = "legendary"
|
||||
badgeString = component.strings.Gift_Attribute_Legendary
|
||||
badgeColor = UIColor(rgb: 0xd57e32)
|
||||
case .rare:
|
||||
badgeString = "rare"
|
||||
badgeColor = UIColor(rgb: 0x79993d)
|
||||
badgeString = component.strings.Gift_Attribute_Rare
|
||||
badgeColor = UIColor(rgb: 0x25a3b9)
|
||||
case .uncommon:
|
||||
badgeString = component.strings.Gift_Attribute_Uncommon
|
||||
badgeColor = UIColor(rgb: 0x22b447)
|
||||
}
|
||||
|
||||
var badgeTextColor = isColored ? .white : component.theme.list.itemSecondaryTextColor
|
||||
var badgeBackgroundColor = isColored ? UIColor(white: 0.0, alpha: 0.2) : component.theme.list.itemPrimaryTextColor.withMultipliedAlpha(0.06)
|
||||
if let badgeColor {
|
||||
badgeTextColor = badgeColor
|
||||
badgeBackgroundColor = badgeColor.withMultipliedAlpha(0.1)
|
||||
if let backgroundColor {
|
||||
badgeBackgroundColor = badgeColor.mixedWith(backgroundColor, alpha: 0.9)
|
||||
} else {
|
||||
badgeBackgroundColor = badgeColor.withMultipliedAlpha(0.1)
|
||||
}
|
||||
}
|
||||
let badgeTextSize = self.badgeText.update(
|
||||
transition: .spring(duration: 0.2),
|
||||
|
|
|
|||
|
|
@ -139,7 +139,7 @@ public final class GiftLoadingShimmerView: UIView {
|
|||
|
||||
required init?(coder: NSCoder) { fatalError() }
|
||||
|
||||
public func update(size: CGSize, theme: PresentationTheme, showFilters: Bool = false, isPlain: Bool = false, transition: ContainedViewLayoutTransition) {
|
||||
public func update(size: CGSize, theme: PresentationTheme, itemSize: CGSize? = nil, showFilters: Bool = false, isPlain: Bool = false, transition: ContainedViewLayoutTransition) {
|
||||
let backgroundColor = isPlain ? theme.list.itemBlocksBackgroundColor : theme.list.blocksBackgroundColor
|
||||
let color = theme.list.itemSecondaryTextColor.mixedWith(theme.list.blocksBackgroundColor, alpha: 0.85)
|
||||
|
||||
|
|
@ -169,7 +169,7 @@ public final class GiftLoadingShimmerView: UIView {
|
|||
|
||||
let optionSpacing: CGFloat = 10.0
|
||||
let optionWidth = (size.width - sideInset * 2.0 - optionSpacing * 2.0) / 3.0
|
||||
let itemSize = CGSize(width: optionWidth, height: 154.0)
|
||||
let itemSize = itemSize ?? CGSize(width: optionWidth, height: 154.0)
|
||||
|
||||
context.setBlendMode(.copy)
|
||||
context.setFillColor(UIColor.clear.cgColor)
|
||||
|
|
|
|||
|
|
@ -1605,7 +1605,7 @@ private final class GiftSetupScreenComponent: Component {
|
|||
guard let self, let component = self.component, let controller = self.environment?.controller(), let upgradePreview else {
|
||||
return
|
||||
}
|
||||
let previewController = component.context.sharedContext.makeGiftUpgradePreviewScreen(context: component.context, attributes: upgradePreview.attributes, peerName: peerName)
|
||||
let previewController = component.context.sharedContext.makeGiftUpgradePreviewScreen(context: component.context, gift: gift, attributes: upgradePreview.attributes, peerName: peerName)
|
||||
controller.push(previewController)
|
||||
})
|
||||
}
|
||||
|
|
|
|||
|
|
@ -24,6 +24,7 @@ swift_library(
|
|||
"//submodules/AccountContext",
|
||||
"//submodules/AppBundle",
|
||||
"//submodules/ItemListUI",
|
||||
"//submodules/ContextUI",
|
||||
"//submodules/TelegramStringFormatting",
|
||||
"//submodules/PresentationDataUtils",
|
||||
"//submodules/Components/SheetComponent",
|
||||
|
|
|
|||
|
|
@ -0,0 +1,174 @@
|
|||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import ComponentFlow
|
||||
import SwiftSignalKit
|
||||
import TelegramCore
|
||||
import TelegramPresentationData
|
||||
import AccountContext
|
||||
import MultilineTextComponent
|
||||
import MultilineTextWithEntitiesComponent
|
||||
import TextFormat
|
||||
import TelegramStringFormatting
|
||||
import Markdown
|
||||
|
||||
final class BalanceComponent: Component {
|
||||
private let context: AccountContext
|
||||
private let theme: PresentationTheme
|
||||
private let action: () -> Void
|
||||
|
||||
init(
|
||||
context: AccountContext,
|
||||
theme: PresentationTheme,
|
||||
action: @escaping () -> Void
|
||||
) {
|
||||
self.context = context
|
||||
self.theme = theme
|
||||
self.action = action
|
||||
}
|
||||
|
||||
static func ==(lhs: BalanceComponent, rhs: BalanceComponent) -> Bool {
|
||||
if lhs.theme !== rhs.theme {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
public final class View: HighlightTrackingButton {
|
||||
private var text = ComponentView<Empty>()
|
||||
|
||||
private var component: BalanceComponent?
|
||||
private var componentState: EmptyComponentState?
|
||||
|
||||
private var starsBalance: Int64 = 0
|
||||
private var tonBalance: Int64 = 0
|
||||
private var balanceDisposable: Disposable?
|
||||
|
||||
override public init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
self.addTarget(self, action: #selector(self.pressed), for: .touchUpInside)
|
||||
}
|
||||
|
||||
required public init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
deinit {
|
||||
self.balanceDisposable?.dispose()
|
||||
}
|
||||
|
||||
@objc private func pressed() {
|
||||
guard let component = self.component else {
|
||||
return
|
||||
}
|
||||
component.action()
|
||||
}
|
||||
|
||||
private var isUpdating = false
|
||||
func update(component: BalanceComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
self.isUpdating = true
|
||||
defer {
|
||||
self.isUpdating = false
|
||||
}
|
||||
self.component = component
|
||||
self.componentState = state
|
||||
|
||||
if self.balanceDisposable == nil {
|
||||
if let starsContext = component.context.starsContext, let tonContext = component.context.tonContext {
|
||||
self.balanceDisposable = combineLatest(queue: Queue.mainQueue(),
|
||||
starsContext.state,
|
||||
tonContext.state
|
||||
).start(next: { [weak self] starsState, tonState in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.starsBalance = starsState?.balance.value ?? 0
|
||||
self.tonBalance = tonState?.balance.value ?? 0
|
||||
if !self.isUpdating {
|
||||
self.componentState?.updated()
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
var rawString: String = ""
|
||||
let starsBalanceString = "**⭐️\(presentationStringsFormattedNumber(Int32(clamping: self.starsBalance), presentationData.dateTimeFormat.groupingSeparator))**"
|
||||
if self.tonBalance > 0 {
|
||||
let tonBalanceString = "**💎\(formatTonAmountText(self.tonBalance, dateTimeFormat: presentationData.dateTimeFormat))**"
|
||||
rawString = starsBalanceString + "\n" + tonBalanceString
|
||||
} else {
|
||||
rawString = presentationData.strings.Stars_Purchase_Balance + "\n" + starsBalanceString
|
||||
}
|
||||
|
||||
let attributedText = parseMarkdownIntoAttributedString(
|
||||
rawString,
|
||||
attributes: MarkdownAttributes(
|
||||
body: MarkdownAttributeSet(font: Font.regular(12.0), textColor: component.theme.rootController.navigationBar.primaryTextColor),
|
||||
bold: MarkdownAttributeSet(font: Font.semibold(12.0), textColor: component.theme.rootController.navigationBar.primaryTextColor),
|
||||
link: MarkdownAttributeSet(font: Font.regular(12.0), textColor: component.theme.rootController.navigationBar.primaryTextColor),
|
||||
linkAttribute: { _ in
|
||||
return nil
|
||||
}
|
||||
),
|
||||
textAlignment: .right
|
||||
).mutableCopy() as! NSMutableAttributedString
|
||||
let starRange = (attributedText.string as NSString).range(of: "⭐️")
|
||||
if starRange.location != NSNotFound {
|
||||
attributedText.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .stars(tinted: false)), range: starRange)
|
||||
attributedText.addAttribute(.baselineOffset, value: 1.0, range: starRange)
|
||||
}
|
||||
let tonRange = (attributedText.string as NSString).range(of: "💎")
|
||||
if tonRange.location != NSNotFound {
|
||||
attributedText.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(interactivelySelectedFromPackId: nil, fileId: 0, file: nil, custom: .ton(tinted: false)), range: tonRange)
|
||||
attributedText.addAttribute(.baselineOffset, value: 1.0, range: tonRange)
|
||||
}
|
||||
|
||||
let textSize = self.text.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(
|
||||
MultilineTextWithEntitiesComponent(
|
||||
context: component.context,
|
||||
animationCache: component.context.animationCache,
|
||||
animationRenderer: component.context.animationRenderer,
|
||||
placeholderColor: .white,
|
||||
text: .plain(attributedText),
|
||||
horizontalAlignment: .right,
|
||||
maximumNumberOfLines: 2,
|
||||
lineSpacing: 0.1,
|
||||
displaysAsynchronously: false
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: availableSize
|
||||
)
|
||||
|
||||
let inset: CGFloat = 12.0
|
||||
let size = CGSize(width: textSize.width + inset * 2.0, height: 44.0)
|
||||
|
||||
if let textView = self.text.view {
|
||||
if textView.superview == nil {
|
||||
textView.isUserInteractionEnabled = false
|
||||
self.addSubview(textView)
|
||||
|
||||
if !transition.animation.isImmediate {
|
||||
transition.animateAlpha(view: textView, from: 0.0, to: 1.0)
|
||||
}
|
||||
}
|
||||
textView.frame = CGRect(origin: CGPoint(x: inset, y: 8.0 - UIScreenPixel), size: textSize)
|
||||
}
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
@ -29,6 +29,7 @@ import LottieComponent
|
|||
import GiftLoadingShimmerView
|
||||
import EdgeEffect
|
||||
import GlassBackgroundComponent
|
||||
import ContextUI
|
||||
|
||||
private let minimumCountToDisplayFilters = 18
|
||||
|
||||
|
|
@ -45,6 +46,7 @@ public final class GiftStoreContentComponent: Component {
|
|||
let starsContext: StarsContext
|
||||
let peerId: EnginePeer.Id
|
||||
let gift: StarGift.Gift
|
||||
let isPlain: Bool
|
||||
let confirmPurchaseImmediately: Bool
|
||||
let starsTopUpOptions: Signal<[StarsTopUpOption]?, NoError>?
|
||||
let scrollToTop: () -> Void
|
||||
|
|
@ -64,6 +66,7 @@ public final class GiftStoreContentComponent: Component {
|
|||
starsContext: StarsContext,
|
||||
peerId: EnginePeer.Id,
|
||||
gift: StarGift.Gift,
|
||||
isPlain: Bool,
|
||||
confirmPurchaseImmediately: Bool,
|
||||
starsTopUpOptions: Signal<[StarsTopUpOption]?, NoError>?,
|
||||
scrollToTop: @escaping () -> Void,
|
||||
|
|
@ -82,6 +85,7 @@ public final class GiftStoreContentComponent: Component {
|
|||
self.starsContext = starsContext
|
||||
self.peerId = peerId
|
||||
self.gift = gift
|
||||
self.isPlain = isPlain
|
||||
self.confirmPurchaseImmediately = confirmPurchaseImmediately
|
||||
self.starsTopUpOptions = starsTopUpOptions
|
||||
self.scrollToTop = scrollToTop
|
||||
|
|
@ -1075,7 +1079,7 @@ public final class GiftStoreContentComponent: Component {
|
|||
|
||||
let loadingSize = CGSize(width: availableSize.width, height: min(1000.0, availableSize.height))
|
||||
if isLoading && self.showLoading {
|
||||
self.loadingView.update(size: loadingSize, theme: component.theme, showFilters: !showingFilters, isPlain: true, transition: .immediate)
|
||||
self.loadingView.update(size: loadingSize, theme: component.theme, showFilters: !showingFilters, isPlain: component.isPlain, transition: .immediate)
|
||||
loadingTransition.setAlpha(view: self.loadingView, alpha: 1.0)
|
||||
} else {
|
||||
loadingTransition.setAlpha(view: self.loadingView, alpha: 0.0)
|
||||
|
|
@ -1145,10 +1149,8 @@ final class GiftStoreScreenComponent: Component {
|
|||
|
||||
private let edgeEffectView: EdgeEffectView
|
||||
|
||||
private let balanceBackgroundView: GlassBackgroundView
|
||||
private let balanceTitle = ComponentView<Empty>()
|
||||
private let balanceValue = ComponentView<Empty>()
|
||||
private let balanceIcon = ComponentView<Empty>()
|
||||
private let balance = ComponentView<Empty>()
|
||||
private let balanceBackgroundView: GlassContextExtractableContainer
|
||||
|
||||
private let title = ComponentView<Empty>()
|
||||
private let subtitle = ComponentView<Empty>()
|
||||
|
|
@ -1165,7 +1167,7 @@ final class GiftStoreScreenComponent: Component {
|
|||
private var isUpdating: Bool = false
|
||||
|
||||
override init(frame: CGRect) {
|
||||
self.balanceBackgroundView = GlassBackgroundView()
|
||||
self.balanceBackgroundView = GlassContextExtractableContainer()
|
||||
|
||||
self.scrollView = ScrollView()
|
||||
self.scrollView.showsVerticalScrollIndicator = true
|
||||
|
|
@ -1211,6 +1213,68 @@ final class GiftStoreScreenComponent: Component {
|
|||
contentView.updateScrolling(bounds: bounds, interactive: interactive, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
func presentBalanceMenu() {
|
||||
guard let component = self.component, let starsContext = component.context.starsContext, let tonContext = component.context.tonContext, let controller = self.environment?.controller() else {
|
||||
return
|
||||
}
|
||||
let tonBalance = tonContext.currentState?.balance.value ?? 0
|
||||
if tonBalance == 0 {
|
||||
let controller = component.context.sharedContext.makeStarsTransactionsScreen(context: component.context, starsContext: tonContext)
|
||||
controller.push(controller)
|
||||
return
|
||||
}
|
||||
|
||||
let presentationData = component.context.sharedContext.currentPresentationData.with { $0 }
|
||||
|
||||
let sourceView = self.balanceBackgroundView
|
||||
|
||||
let items: Signal<[ContextMenuItem], NoError> = combineLatest(
|
||||
queue: Queue.mainQueue(),
|
||||
starsContext.state,
|
||||
tonContext.state
|
||||
)
|
||||
|> take(1)
|
||||
|> map { starsState, tonState -> [ContextMenuItem] in
|
||||
let starsBalance = starsState?.balance ?? .zero
|
||||
let tonBalance = tonState?.balance.value ?? 0
|
||||
|
||||
var items: [ContextMenuItem] = []
|
||||
|
||||
items.append(.action(ContextMenuActionItem(
|
||||
text: "My Stars",
|
||||
textLayout: .secondLineWithValue(formatStarsAmountText(starsBalance, dateTimeFormat: presentationData.dateTimeFormat)),
|
||||
icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Stars"), color: theme.contextMenu.primaryColor) },
|
||||
action: { [weak self] _, f in
|
||||
f(.dismissWithoutContent)
|
||||
guard let self, let component = self.component, let environment = self.environment else {
|
||||
return
|
||||
}
|
||||
let controller = component.context.sharedContext.makeStarsTransactionsScreen(context: component.context, starsContext: starsContext)
|
||||
environment.controller()?.push(controller)
|
||||
}
|
||||
)))
|
||||
|
||||
items.append(.action(ContextMenuActionItem(
|
||||
text: "My TON",
|
||||
textLayout: .secondLineWithValue(formatTonAmountText(tonBalance, dateTimeFormat: presentationData.dateTimeFormat)),
|
||||
icon: { theme in return generateTintedImage(image: UIImage(bundleImageName: "Chat/Context Menu/Ton"), color: theme.contextMenu.primaryColor) },
|
||||
action: { [weak self] _, f in
|
||||
f(.dismissWithoutContent)
|
||||
guard let self, let component = self.component, let environment = self.environment else {
|
||||
return
|
||||
}
|
||||
let controller = component.context.sharedContext.makeStarsTransactionsScreen(context: component.context, starsContext: tonContext)
|
||||
environment.controller()?.push(controller)
|
||||
}
|
||||
)))
|
||||
|
||||
return items
|
||||
}
|
||||
|
||||
let contextController = makeContextController(presentationData: presentationData, source: .reference(GiftStoreReferenceContentSource(controller: controller, sourceView: sourceView)), items: items |> map { ContextController.Items(content: .list($0)) }, gesture: nil)
|
||||
controller.presentInGlobalOverlay(contextController)
|
||||
}
|
||||
|
||||
func update(component: GiftStoreScreenComponent, availableSize: CGSize, state: State, environment: Environment<EnvironmentType>, transition: ComponentTransition) -> CGSize {
|
||||
self.isUpdating = true
|
||||
|
|
@ -1255,42 +1319,37 @@ final class GiftStoreScreenComponent: Component {
|
|||
let edgeEffectFrame = CGRect(origin: CGPoint(x: 0.0, y: 0.0), size: CGSize(width: availableSize.width, height: edgeEffectHeight))
|
||||
transition.setFrame(view: self.edgeEffectView, frame: edgeEffectFrame)
|
||||
self.edgeEffectView.update(content: environment.theme.list.blocksBackgroundColor, blur: true, rect: edgeEffectFrame, edge: .top, edgeSize: min(30, edgeEffectFrame.height), transition: transition)
|
||||
|
||||
let balanceTitleSize = self.balanceTitle.update(
|
||||
|
||||
|
||||
let balanceSize = self.balance.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: strings.Stars_Purchase_Balance,
|
||||
font: Font.regular(14.0),
|
||||
textColor: environment.theme.actionSheet.primaryTextColor
|
||||
)),
|
||||
maximumNumberOfLines: 1
|
||||
)),
|
||||
component: AnyComponent(
|
||||
BalanceComponent(
|
||||
context: component.context,
|
||||
theme: environment.theme,
|
||||
action: { [weak self] in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
self.presentBalanceMenu()
|
||||
}
|
||||
)
|
||||
),
|
||||
environment: {},
|
||||
containerSize: availableSize
|
||||
)
|
||||
|
||||
let formattedBalance = formatStarsAmountText(self.starsState?.balance ?? StarsAmount.zero, dateTimeFormat: environment.dateTimeFormat)
|
||||
let smallLabelFont = Font.regular(11.0)
|
||||
let labelFont = Font.semibold(14.0)
|
||||
let balanceText = tonAmountAttributedString(formattedBalance, integralFont: labelFont, fractionalFont: smallLabelFont, color: environment.theme.actionSheet.primaryTextColor, decimalSeparator: environment.dateTimeFormat.decimalSeparator)
|
||||
|
||||
let balanceValueSize = self.balanceValue.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(balanceText),
|
||||
maximumNumberOfLines: 1
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: availableSize
|
||||
)
|
||||
let balanceIconSize = self.balanceIcon.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(BundleIconComponent(name: "Premium/Stars/StarSmall", tintColor: nil)),
|
||||
environment: {},
|
||||
containerSize: availableSize
|
||||
)
|
||||
|
||||
let balanceFrame = CGRect(origin: .zero, size: balanceSize)
|
||||
if let balanceView = self.balance.view {
|
||||
if balanceView.superview == nil {
|
||||
self.balanceBackgroundView.contentView.addSubview(balanceView)
|
||||
}
|
||||
balanceView.frame = balanceFrame
|
||||
|
||||
let balanceBackgroundFrame = CGRect(origin: CGPoint(x: availableSize.width - environment.safeInsets.right - 16.0 - balanceSize.width, y: environment.navigationHeight - 60.0 + 2.0 + floor((60.0 - 44.0) * 0.5)), size: balanceSize)
|
||||
|
||||
transition.setFrame(view: self.balanceBackgroundView, frame: balanceBackgroundFrame)
|
||||
self.balanceBackgroundView.update(size: balanceBackgroundFrame.size, cornerRadius: balanceBackgroundFrame.height * 0.5, isDark: environment.theme.overallDarkAppearance, tintColor: .init(kind: .panel), isInteractive: true, transition: transition)
|
||||
}
|
||||
if self.balanceBackgroundView.superview == nil {
|
||||
component.overNavigationContainer.addSubview(self.balanceBackgroundView)
|
||||
}
|
||||
|
|
@ -1300,32 +1359,6 @@ final class GiftStoreScreenComponent: Component {
|
|||
topInset = environment.statusBarHeight - 6.0
|
||||
}
|
||||
|
||||
if let balanceTitleView = self.balanceTitle.view, let balanceValueView = self.balanceValue.view, let balanceIconView = self.balanceIcon.view {
|
||||
if balanceTitleView.superview == nil {
|
||||
self.balanceBackgroundView.contentView.addSubview(balanceTitleView)
|
||||
self.balanceBackgroundView.contentView.addSubview(balanceValueView)
|
||||
self.balanceBackgroundView.contentView.addSubview(balanceIconView)
|
||||
}
|
||||
|
||||
let topBalanceOriginY = (44.0 - balanceTitleSize.height - balanceValueSize.height) / 2.0
|
||||
|
||||
let balanceSideInset: CGFloat = 12.0
|
||||
var balanceBackgroundSize = CGSize(width: balanceTitleSize.width + balanceSideInset * 2.0, height: 44.0)
|
||||
balanceBackgroundSize.width = max(balanceBackgroundSize.width, balanceValueSize.width + balanceIconSize.width + 2.0 + balanceSideInset * 2.0)
|
||||
|
||||
let balanceBackgroundFrame = CGRect(origin: CGPoint(x: availableSize.width - environment.safeInsets.right - 16.0 - balanceBackgroundSize.width, y: environment.navigationHeight - 60.0 + 2.0 + floor((60.0 - 44.0) * 0.5)), size: balanceBackgroundSize)
|
||||
|
||||
transition.setFrame(view: self.balanceBackgroundView, frame: balanceBackgroundFrame)
|
||||
self.balanceBackgroundView.update(size: balanceBackgroundFrame.size, cornerRadius: balanceBackgroundFrame.height * 0.5, isDark: environment.theme.overallDarkAppearance, tintColor: .init(kind: .panel), transition: transition)
|
||||
|
||||
balanceTitleView.center = CGPoint(x: balanceBackgroundFrame.width - balanceSideInset - balanceTitleSize.width / 2.0, y: topBalanceOriginY + balanceTitleSize.height / 2.0)
|
||||
balanceTitleView.bounds = CGRect(origin: .zero, size: balanceTitleSize)
|
||||
balanceValueView.center = CGPoint(x: balanceBackgroundFrame.width - balanceSideInset - balanceValueSize.width / 2.0, y: topBalanceOriginY + balanceTitleSize.height + balanceValueSize.height / 2.0)
|
||||
balanceValueView.bounds = CGRect(origin: .zero, size: balanceValueSize)
|
||||
balanceIconView.center = CGPoint(x: balanceBackgroundFrame.width - balanceSideInset - balanceValueSize.width - balanceIconSize.width / 2.0 - 2.0, y: topBalanceOriginY + balanceTitleSize.height + balanceValueSize.height / 2.0 - UIScreenPixel)
|
||||
balanceIconView.bounds = CGRect(origin: .zero, size: balanceIconSize)
|
||||
}
|
||||
|
||||
let titleSize = self.title.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
|
|
@ -1360,6 +1393,7 @@ final class GiftStoreScreenComponent: Component {
|
|||
starsContext: component.starsContext,
|
||||
peerId: component.peerId,
|
||||
gift: component.gift,
|
||||
isPlain: false,
|
||||
confirmPurchaseImmediately: false,
|
||||
starsTopUpOptions: nil,
|
||||
scrollToTop: { [weak self] in
|
||||
|
|
|
|||
|
|
@ -1,267 +0,0 @@
|
|||
import Foundation
|
||||
import UIKit
|
||||
import Display
|
||||
import AsyncDisplayKit
|
||||
import ComponentFlow
|
||||
import TelegramCore
|
||||
import BundleIconComponent
|
||||
import MultilineTextComponent
|
||||
import MoreButtonNode
|
||||
import AccountContext
|
||||
import TelegramPresentationData
|
||||
import TelegramStringFormatting
|
||||
|
||||
final class PriceButtonComponent: Component {
|
||||
let price: CurrencyAmount
|
||||
let dateTimeFormat: PresentationDateTimeFormat
|
||||
|
||||
init(
|
||||
price: CurrencyAmount,
|
||||
dateTimeFormat: PresentationDateTimeFormat
|
||||
) {
|
||||
self.price = price
|
||||
self.dateTimeFormat = dateTimeFormat
|
||||
}
|
||||
|
||||
static func ==(lhs: PriceButtonComponent, rhs: PriceButtonComponent) -> Bool {
|
||||
return lhs.price == rhs.price && lhs.dateTimeFormat == rhs.dateTimeFormat
|
||||
}
|
||||
|
||||
final class View: UIView {
|
||||
private let backgroundView = UIView()
|
||||
|
||||
private let icon = UIImageView()
|
||||
private let text = ComponentView<Empty>()
|
||||
|
||||
private var component: PriceButtonComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
self.backgroundView.clipsToBounds = true
|
||||
self.addSubview(self.backgroundView)
|
||||
|
||||
self.backgroundView.addSubview(self.icon)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
func update(component: PriceButtonComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
let previousComponent = self.component
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
if previousComponent?.price.currency != component.price.currency {
|
||||
switch component.price.currency {
|
||||
case .stars:
|
||||
self.icon.image = UIImage(bundleImageName: "Premium/Stars/ButtonStar")?.withRenderingMode(.alwaysTemplate)
|
||||
case .ton:
|
||||
self.icon.image = UIImage(bundleImageName: "Ads/TonAbout")?.withRenderingMode(.alwaysTemplate)
|
||||
}
|
||||
}
|
||||
|
||||
var backgroundSize = CGSize(width: 42.0, height: 30.0)
|
||||
let textSize = self.text.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: formatCurrencyAmountText(component.price, dateTimeFormat: component.dateTimeFormat),
|
||||
font: Font.semibold(11.0),
|
||||
textColor: UIColor(rgb: 0xffffff)
|
||||
))
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: availableSize
|
||||
)
|
||||
let textFrame = CGRect(origin: CGPoint(x: 32.0, y: floorToScreenPixels((backgroundSize.height - textSize.height) / 2.0)), size: textSize)
|
||||
if let textView = self.text.view {
|
||||
if textView.superview == nil {
|
||||
self.backgroundView.addSubview(textView)
|
||||
}
|
||||
transition.setFrame(view: textView, frame: textFrame)
|
||||
}
|
||||
backgroundSize.width += textSize.width
|
||||
|
||||
self.backgroundView.layer.cornerRadius = backgroundSize.height / 2.0
|
||||
|
||||
let backgroundColor: UIColor = UIColor(rgb: 0xffffff, alpha: 0.1)
|
||||
transition.setBackgroundColor(view: self.backgroundView, color: backgroundColor)
|
||||
|
||||
let backgroundFrame = CGRect(origin: .zero, size: backgroundSize)
|
||||
transition.setFrame(view: self.backgroundView, frame: backgroundFrame)
|
||||
|
||||
if let iconSize = self.icon.image?.size {
|
||||
let iconFrame = CGRect(origin: CGPoint(x: 12.0, y: floorToScreenPixels((backgroundSize.height - iconSize.height) / 2.0)), size: iconSize)
|
||||
transition.setFrame(view: self.icon, frame: iconFrame)
|
||||
}
|
||||
self.icon.tintColor = UIColor(rgb: 0xffffff)
|
||||
|
||||
return backgroundSize
|
||||
}
|
||||
}
|
||||
|
||||
func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
final class ButtonsComponent: Component {
|
||||
let theme: PresentationTheme
|
||||
let isOverlay: Bool
|
||||
let showMoreButton: Bool
|
||||
let closePressed: () -> Void
|
||||
let morePressed: (ASDisplayNode, ContextGesture?) -> Void
|
||||
|
||||
init(
|
||||
theme: PresentationTheme,
|
||||
isOverlay: Bool,
|
||||
showMoreButton: Bool,
|
||||
closePressed: @escaping () -> Void,
|
||||
morePressed: @escaping (ASDisplayNode, ContextGesture?) -> Void
|
||||
) {
|
||||
self.theme = theme
|
||||
self.isOverlay = isOverlay
|
||||
self.showMoreButton = showMoreButton
|
||||
self.closePressed = closePressed
|
||||
self.morePressed = morePressed
|
||||
}
|
||||
|
||||
static func ==(lhs: ButtonsComponent, rhs: ButtonsComponent) -> Bool {
|
||||
return lhs.theme === rhs.theme && lhs.isOverlay == rhs.isOverlay && lhs.showMoreButton == rhs.showMoreButton
|
||||
}
|
||||
|
||||
final class View: UIView {
|
||||
private let backgroundView = UIView()
|
||||
|
||||
private let closeButton = HighlightTrackingButton()
|
||||
private let closeIcon = UIImageView()
|
||||
private let moreNode = MoreButtonNode(theme: defaultPresentationTheme, size: CGSize(width: 36.0, height: 36.0), encircled: false)
|
||||
|
||||
private var component: ButtonsComponent?
|
||||
private weak var state: EmptyComponentState?
|
||||
|
||||
override init(frame: CGRect) {
|
||||
super.init(frame: frame)
|
||||
|
||||
self.backgroundView.clipsToBounds = true
|
||||
self.addSubview(self.backgroundView)
|
||||
|
||||
self.closeIcon.image = generateCloseButtonImage()
|
||||
self.moreNode.updateColor(.white, transition: .immediate)
|
||||
|
||||
self.backgroundView.addSubview(self.moreNode.view)
|
||||
self.backgroundView.addSubview(self.closeButton)
|
||||
self.backgroundView.addSubview(self.closeIcon)
|
||||
|
||||
self.closeButton.highligthedChanged = { [weak self] highlighted in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
if highlighted {
|
||||
self.closeIcon.layer.removeAnimation(forKey: "opacity")
|
||||
self.closeIcon.alpha = 0.6
|
||||
} else {
|
||||
self.closeIcon.alpha = 1.0
|
||||
self.closeIcon.layer.animateAlpha(from: 0.6, to: 1.0, duration: 0.2)
|
||||
}
|
||||
}
|
||||
self.closeButton.addTarget(self, action: #selector(self.closePressed), for: .touchUpInside)
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
fatalError("init(coder:) has not been implemented")
|
||||
}
|
||||
|
||||
@objc private func closePressed() {
|
||||
guard let component = self.component else {
|
||||
return
|
||||
}
|
||||
component.closePressed()
|
||||
}
|
||||
|
||||
func update(component: ButtonsComponent, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
self.component = component
|
||||
self.state = state
|
||||
|
||||
let backgroundSize = CGSize(width: component.showMoreButton ? 70.0 : 30.0, height: 30.0)
|
||||
self.backgroundView.layer.cornerRadius = backgroundSize.height / 2.0
|
||||
|
||||
let backgroundColor: UIColor = component.isOverlay ? UIColor(rgb: 0xffffff, alpha: 0.1) : UIColor(rgb: 0x808084, alpha: 0.1)
|
||||
let foregroundColor: UIColor = component.isOverlay ? .white : component.theme.actionSheet.inputClearButtonColor
|
||||
transition.setBackgroundColor(view: self.backgroundView, color: backgroundColor)
|
||||
transition.setTintColor(view: self.closeIcon, color: foregroundColor)
|
||||
|
||||
let backgroundFrame = CGRect(origin: .zero, size: backgroundSize)
|
||||
transition.setFrame(view: self.backgroundView, frame: backgroundFrame)
|
||||
|
||||
transition.setFrame(view: self.moreNode.view, frame: CGRect(origin: CGPoint(x: -7.0, y: -4.0), size: CGSize(width: 36.0, height: 36.0)))
|
||||
transition.setAlpha(view: self.moreNode.view, alpha: component.showMoreButton ? 1.0 : 0.0)
|
||||
self.moreNode.action = { [weak self] node, gesture in
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
component.morePressed(node, gesture)
|
||||
}
|
||||
|
||||
let closeFrame = CGRect(origin: CGPoint(x: backgroundSize.width - 30.0 - (component.showMoreButton ? 3.0 : 0.0), y: 0.0), size: CGSize(width: 30.0, height: 30.0))
|
||||
transition.setFrame(view: self.closeIcon, frame: closeFrame)
|
||||
transition.setFrame(view: self.closeButton, frame: closeFrame)
|
||||
|
||||
return backgroundSize
|
||||
}
|
||||
}
|
||||
|
||||
func makeView() -> View {
|
||||
return View(frame: CGRect())
|
||||
}
|
||||
|
||||
func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment<Empty>, transition: ComponentTransition) -> CGSize {
|
||||
return view.update(component: self, availableSize: availableSize, state: state, environment: environment, transition: transition)
|
||||
}
|
||||
}
|
||||
|
||||
private func generateCloseButtonImage() -> UIImage? {
|
||||
return generateImage(CGSize(width: 30.0, height: 30.0), contextGenerator: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
context.setLineWidth(2.0)
|
||||
context.setLineCap(.round)
|
||||
context.setStrokeColor(UIColor.white.cgColor)
|
||||
|
||||
context.move(to: CGPoint(x: 10.0, y: 10.0))
|
||||
context.addLine(to: CGPoint(x: 20.0, y: 20.0))
|
||||
context.strokePath()
|
||||
|
||||
context.move(to: CGPoint(x: 20.0, y: 10.0))
|
||||
context.addLine(to: CGPoint(x: 10.0, y: 20.0))
|
||||
context.strokePath()
|
||||
})?.withRenderingMode(.alwaysTemplate)
|
||||
}
|
||||
|
||||
func generateCloseButtonImage(backgroundColor: UIColor, foregroundColor: UIColor) -> UIImage? {
|
||||
return generateImage(CGSize(width: 30.0, height: 30.0), contextGenerator: { size, context in
|
||||
context.clear(CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
context.setFillColor(backgroundColor.cgColor)
|
||||
context.fillEllipse(in: CGRect(origin: CGPoint(), size: size))
|
||||
|
||||
context.setLineWidth(2.0)
|
||||
context.setLineCap(.round)
|
||||
context.setStrokeColor(foregroundColor.cgColor)
|
||||
|
||||
context.move(to: CGPoint(x: 10.0, y: 10.0))
|
||||
context.addLine(to: CGPoint(x: 20.0, y: 20.0))
|
||||
context.strokePath()
|
||||
|
||||
context.move(to: CGPoint(x: 20.0, y: 10.0))
|
||||
context.addLine(to: CGPoint(x: 10.0, y: 20.0))
|
||||
context.strokePath()
|
||||
})
|
||||
}
|
||||
|
|
@ -746,8 +746,8 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
|
|||
} else {
|
||||
return false
|
||||
}
|
||||
}), case let .backdrop(_, _, innerColor, _, _, _, _) = backdropAttribute {
|
||||
buttonColor = UIColor(rgb: UInt32(bitPattern: innerColor)).withMultipliedBrightnessBy(1.05)
|
||||
}), case let .backdrop(_, _, innerColor, outerColor, _, _, _) = backdropAttribute {
|
||||
buttonColor = UIColor(rgb: UInt32(bitPattern: outerColor)).mixedWith(.white, alpha: 0.2)
|
||||
secondaryTextColor = UIColor(rgb: UInt32(bitPattern: innerColor)).withMultiplied(hue: 1.0, saturation: 1.02, brightness: 1.25).mixedWith(UIColor.white, alpha: 0.3)
|
||||
}
|
||||
|
||||
|
|
@ -1083,7 +1083,7 @@ private final class GiftAuctionViewSheetContent: CombinedComponent {
|
|||
guard let state, let attributes = state.giftUpgradeAttributes else {
|
||||
return
|
||||
}
|
||||
let variantsController = component.context.sharedContext.makeGiftUpgradeVariantsScreen(context: component.context, gift: .generic(gift), onlyCrafted: false, attributes: attributes, selectedAttributes: nil, focusedAttribute: nil)
|
||||
let variantsController = component.context.sharedContext.makeGiftUpgradeVariantsScreen(context: component.context, gift: .generic(gift), crafted: false, attributes: attributes, selectedAttributes: nil, focusedAttribute: nil)
|
||||
environment.controller()?.push(variantsController)
|
||||
}, animateScale: false),
|
||||
availableSize: CGSize(width: context.availableSize.width - 64.0, height: context.availableSize.height),
|
||||
|
|
|
|||
|
|
@ -294,8 +294,8 @@ private final class GiftAuctionWearPreviewSheetContent: CombinedComponent {
|
|||
} else {
|
||||
return false
|
||||
}
|
||||
}), case let .backdrop(_, _, innerColor, _, _, _, _) = backdropAttribute {
|
||||
buttonColor = UIColor(rgb: UInt32(bitPattern: innerColor)).withMultipliedBrightnessBy(1.05)
|
||||
}), case let .backdrop(_, _, innerColor, outerColor, _, _, _) = backdropAttribute {
|
||||
buttonColor = UIColor(rgb: UInt32(bitPattern: outerColor)).mixedWith(.white, alpha: 0.2)
|
||||
secondaryTextColor = UIColor(rgb: UInt32(bitPattern: innerColor)).withMultiplied(hue: 1.0, saturation: 1.02, brightness: 1.25).mixedWith(UIColor.white, alpha: 0.3)
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -29,7 +29,7 @@ private final class GiftUpgradeVariantsScreenComponent: Component {
|
|||
|
||||
let context: AccountContext
|
||||
let gift: StarGift
|
||||
let onlyCrafted: Bool
|
||||
let crafted: Bool
|
||||
let attributes: [StarGift.UniqueGift.Attribute]
|
||||
let selectedAttributes: [StarGift.UniqueGift.Attribute]?
|
||||
let focusedAttribute: StarGift.UniqueGift.Attribute?
|
||||
|
|
@ -37,14 +37,14 @@ private final class GiftUpgradeVariantsScreenComponent: Component {
|
|||
init(
|
||||
context: AccountContext,
|
||||
gift: StarGift,
|
||||
onlyCrafted: Bool,
|
||||
crafted: Bool,
|
||||
attributes: [StarGift.UniqueGift.Attribute],
|
||||
selectedAttributes: [StarGift.UniqueGift.Attribute]?,
|
||||
focusedAttribute: StarGift.UniqueGift.Attribute?
|
||||
) {
|
||||
self.context = context
|
||||
self.gift = gift
|
||||
self.onlyCrafted = onlyCrafted
|
||||
self.crafted = crafted
|
||||
self.attributes = attributes
|
||||
self.selectedAttributes = selectedAttributes
|
||||
self.focusedAttribute = focusedAttribute
|
||||
|
|
@ -112,6 +112,7 @@ private final class GiftUpgradeVariantsScreenComponent: Component {
|
|||
private var giftItems: [AnyHashable: ComponentView<Empty>] = [:]
|
||||
|
||||
private var selectedSection: SelectedSection = .models
|
||||
private var displayCraftableModels = false
|
||||
|
||||
private let giftCompositionExternalState = GiftCompositionComponent.ExternalState()
|
||||
|
||||
|
|
@ -122,7 +123,8 @@ private final class GiftUpgradeVariantsScreenComponent: Component {
|
|||
private var previewBackdropIndex: Int = 0
|
||||
private var previewSymbolIndex: Int = 0
|
||||
|
||||
private var previewModels: [StarGift.UniqueGift.Attribute] = []
|
||||
private var previewPrimaryModels: [StarGift.UniqueGift.Attribute] = []
|
||||
private var previewCraftableModels: [StarGift.UniqueGift.Attribute] = []
|
||||
private var previewBackdrops: [StarGift.UniqueGift.Attribute] = []
|
||||
private var previewSymbols: [StarGift.UniqueGift.Attribute] = []
|
||||
|
||||
|
|
@ -130,10 +132,15 @@ private final class GiftUpgradeVariantsScreenComponent: Component {
|
|||
private var selectedBackdrop: StarGift.UniqueGift.Attribute?
|
||||
private var selectedSymbol: StarGift.UniqueGift.Attribute?
|
||||
|
||||
private var modelCount: Int32 = 0
|
||||
private var craftableModelCount: Int32 = 0
|
||||
private var primaryModelCount: Int32 = 0
|
||||
private var backdropCount: Int32 = 0
|
||||
private var symbolCount: Int32 = 0
|
||||
|
||||
private var currentDescriptionHeight: CGFloat = 0.0
|
||||
|
||||
private var cachedSmallChevronImage: (UIImage, PresentationTheme)?
|
||||
|
||||
private var ignoreScrolling: Bool = false
|
||||
|
||||
private var component: GiftUpgradeVariantsScreenComponent?
|
||||
|
|
@ -202,6 +209,8 @@ private final class GiftUpgradeVariantsScreenComponent: Component {
|
|||
self.containerView.addSubview(self.navigationBarContainer)
|
||||
|
||||
self.dimView.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapGesture(_:))))
|
||||
|
||||
self.alpha = 0.0
|
||||
}
|
||||
|
||||
required init?(coder: NSCoder) {
|
||||
|
|
@ -271,6 +280,7 @@ private final class GiftUpgradeVariantsScreenComponent: Component {
|
|||
}
|
||||
|
||||
func animateIn() {
|
||||
self.alpha = 1.0
|
||||
self.dimView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||
let animateOffset: CGFloat = self.bounds.height - self.backgroundLayer.frame.minY
|
||||
self.scrollContentClippingView.layer.animatePosition(from: CGPoint(x: 0.0, y: animateOffset), to: CGPoint(), duration: 0.5, timingFunction: kCAMediaTimingFunctionSpring, additive: true)
|
||||
|
|
@ -290,8 +300,9 @@ private final class GiftUpgradeVariantsScreenComponent: Component {
|
|||
}
|
||||
|
||||
private func previewTimerTick() {
|
||||
guard !self.previewModels.isEmpty else { return }
|
||||
self.previewModelIndex = (self.previewModelIndex + 1) % self.previewModels.count
|
||||
let previewModels = self.displayCraftableModels ? self.previewCraftableModels : self.previewPrimaryModels
|
||||
guard !previewModels.isEmpty else { return }
|
||||
self.previewModelIndex = (self.previewModelIndex + 1) % previewModels.count
|
||||
|
||||
let previousSymbolIndex = self.previewSymbolIndex
|
||||
var randomSymbolIndex = previousSymbolIndex
|
||||
|
|
@ -326,13 +337,15 @@ private final class GiftUpgradeVariantsScreenComponent: Component {
|
|||
}
|
||||
|
||||
private var effectiveGifts: [[StarGift.UniqueGift.Attribute]] = []
|
||||
private func updateEffectiveGifts(attributes: [StarGift.UniqueGift.Attribute], onlyCrafted: Bool) {
|
||||
private func updateEffectiveGifts(attributes: [StarGift.UniqueGift.Attribute]) {
|
||||
var effectiveGifts: [[StarGift.UniqueGift.Attribute]] = []
|
||||
switch self.selectedSection {
|
||||
case .models:
|
||||
let models = Array(attributes.filter({ attribute in
|
||||
if case let .model(_, _, _, crafted) = attribute {
|
||||
if onlyCrafted && !crafted {
|
||||
if self.displayCraftableModels && !crafted {
|
||||
return false
|
||||
} else if !self.displayCraftableModels && crafted {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
|
@ -344,7 +357,8 @@ private final class GiftUpgradeVariantsScreenComponent: Component {
|
|||
effectiveGifts.append([model])
|
||||
}
|
||||
case .backdrops:
|
||||
let selectedModel = self.selectedModel ?? self.previewModels[self.previewModelIndex]
|
||||
let previewModels = self.displayCraftableModels ? self.previewCraftableModels : previewPrimaryModels
|
||||
let selectedModel = self.selectedModel ?? previewModels[self.previewModelIndex]
|
||||
let selectedSymbol = self.selectedSymbol ?? self.previewSymbols[self.previewSymbolIndex]
|
||||
let backdrops = Array(attributes.filter({ attribute in
|
||||
if case .backdrop = attribute {
|
||||
|
|
@ -400,7 +414,7 @@ private final class GiftUpgradeVariantsScreenComponent: Component {
|
|||
let optionWidth = (fillingSize - 16.0 * 2.0 - optionSpacing * 2.0) / 3.0
|
||||
let optionSize = CGSize(width: optionWidth, height: 126.0)
|
||||
|
||||
let topInset: CGFloat = 393.0
|
||||
let topInset: CGFloat = 375.0 + self.currentDescriptionHeight
|
||||
|
||||
var validIds: [AnyHashable] = []
|
||||
var itemFrame = CGRect(origin: CGPoint(x: sideInset, y: topInset + 9.0), size: optionSize)
|
||||
|
|
@ -604,15 +618,19 @@ private final class GiftUpgradeVariantsScreenComponent: Component {
|
|||
let sideInset: CGFloat = rawSideInset + 16.0
|
||||
|
||||
if self.component == nil {
|
||||
var modelCount: Int32 = 0
|
||||
self.displayCraftableModels = component.crafted
|
||||
|
||||
var primaryModelCount: Int32 = 0
|
||||
var craftableModelCount: Int32 = 0
|
||||
var backdropCount: Int32 = 0
|
||||
var symbolCount: Int32 = 0
|
||||
for attribute in component.attributes {
|
||||
switch attribute {
|
||||
case let .model(_, _, _, crafted):
|
||||
if component.onlyCrafted && !crafted {
|
||||
if crafted {
|
||||
craftableModelCount += 1
|
||||
} else {
|
||||
modelCount += 1
|
||||
primaryModelCount += 1
|
||||
}
|
||||
case .backdrop:
|
||||
backdropCount += 1
|
||||
|
|
@ -622,13 +640,14 @@ private final class GiftUpgradeVariantsScreenComponent: Component {
|
|||
break
|
||||
}
|
||||
}
|
||||
self.modelCount = modelCount
|
||||
self.primaryModelCount = primaryModelCount
|
||||
self.craftableModelCount = craftableModelCount
|
||||
self.backdropCount = backdropCount
|
||||
self.symbolCount = symbolCount
|
||||
|
||||
let randomModels = Array(component.attributes.filter({ attribute in
|
||||
let randomPrimaryModels = Array(component.attributes.filter({ attribute in
|
||||
if case let .model(_, _, _, crafted) = attribute {
|
||||
if component.onlyCrafted && !crafted {
|
||||
if crafted {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
|
|
@ -636,7 +655,19 @@ private final class GiftUpgradeVariantsScreenComponent: Component {
|
|||
return false
|
||||
}
|
||||
}).shuffled().prefix(15))
|
||||
self.previewModels = randomModels
|
||||
self.previewPrimaryModels = randomPrimaryModels
|
||||
|
||||
let randomCraftableModels = Array(component.attributes.filter({ attribute in
|
||||
if case let .model(_, _, _, crafted) = attribute {
|
||||
if !crafted {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}).shuffled().prefix(15))
|
||||
self.previewCraftableModels = randomCraftableModels
|
||||
|
||||
let randomBackdrops = Array(component.attributes.filter({ attribute in
|
||||
if case .backdrop = attribute {
|
||||
|
|
@ -684,7 +715,7 @@ private final class GiftUpgradeVariantsScreenComponent: Component {
|
|||
}
|
||||
}
|
||||
|
||||
self.updateEffectiveGifts(attributes: component.attributes, onlyCrafted: component.onlyCrafted)
|
||||
self.updateEffectiveGifts(attributes: component.attributes)
|
||||
}
|
||||
|
||||
self.component = component
|
||||
|
|
@ -705,14 +736,15 @@ private final class GiftUpgradeVariantsScreenComponent: Component {
|
|||
var badgeColor: UIColor = .white.withAlphaComponent(0.4)
|
||||
|
||||
var attributes: [StarGift.UniqueGift.Attribute] = []
|
||||
if !self.previewModels.isEmpty {
|
||||
let previewModels = self.displayCraftableModels ? self.previewCraftableModels : self.previewPrimaryModels
|
||||
if !previewModels.isEmpty {
|
||||
if self.isPlaying {
|
||||
attributes.append(self.previewModels[self.previewModelIndex])
|
||||
attributes.append(previewModels[self.previewModelIndex])
|
||||
attributes.append(self.previewBackdrops[self.previewBackdropIndex])
|
||||
attributes.append(self.previewSymbols[self.previewSymbolIndex])
|
||||
} else {
|
||||
if self.selectedModel == nil {
|
||||
self.selectedModel = self.previewModels[self.previewModelIndex]
|
||||
self.selectedModel = previewModels[self.previewModelIndex]
|
||||
}
|
||||
if self.selectedBackdrop == nil {
|
||||
self.selectedBackdrop = self.previewBackdrops[self.previewBackdropIndex]
|
||||
|
|
@ -739,7 +771,7 @@ private final class GiftUpgradeVariantsScreenComponent: Component {
|
|||
return false
|
||||
}
|
||||
}), case let .backdrop(_, _, innerColor, outerColor, _, _, _) = backdropAttribute {
|
||||
buttonColor = UIColor(rgb: UInt32(bitPattern: innerColor)).withMultipliedBrightnessBy(1.05)
|
||||
buttonColor = UIColor(rgb: UInt32(bitPattern: outerColor)).mixedWith(.white, alpha: 0.2)
|
||||
|
||||
badgeColor = UIColor(rgb: UInt32(bitPattern: innerColor)).withMultipliedBrightnessBy(1.05)
|
||||
let outer = UIColor(rgb: UInt32(bitPattern: outerColor))
|
||||
|
|
@ -786,8 +818,11 @@ private final class GiftUpgradeVariantsScreenComponent: Component {
|
|||
contentHeight += headerSize.height
|
||||
|
||||
var titleText: String = ""
|
||||
if case let .generic(gift) = component.gift {
|
||||
switch component.gift {
|
||||
case let .generic(gift):
|
||||
titleText = gift.title ?? ""
|
||||
case let .unique(gift):
|
||||
titleText = gift.title
|
||||
}
|
||||
|
||||
let titleSize = self.title.update(
|
||||
|
|
@ -838,7 +873,7 @@ private final class GiftUpgradeVariantsScreenComponent: Component {
|
|||
}
|
||||
|
||||
let attributeSpacing: CGFloat = 10.0
|
||||
let attributeWidth: CGFloat = floor((fillingSize - attributeSpacing * CGFloat(attributes.count - 1)) / CGFloat(attributes.count))
|
||||
let attributeWidth: CGFloat = floor((fillingSize - 32.0 - attributeSpacing * CGFloat(attributes.count - 1)) / CGFloat(attributes.count))
|
||||
let attributeHeight: CGFloat = 45.0
|
||||
|
||||
for i in 0 ..< attributes.count {
|
||||
|
|
@ -911,7 +946,7 @@ private final class GiftUpgradeVariantsScreenComponent: Component {
|
|||
self.selectedSection = id
|
||||
self.isPlaying = false
|
||||
|
||||
self.updateEffectiveGifts(attributes: component.attributes, onlyCrafted: component.onlyCrafted)
|
||||
self.updateEffectiveGifts(attributes: component.attributes)
|
||||
self.state?.updated(transition: ComponentTransition(animation: .curve(duration: 0.4, curve: .spring)))
|
||||
})),
|
||||
environment: {},
|
||||
|
|
@ -932,17 +967,22 @@ private final class GiftUpgradeVariantsScreenComponent: Component {
|
|||
let itemHeight: CGFloat = 126.0
|
||||
let itemSpacing: CGFloat = 10.0
|
||||
|
||||
let descriptionText: String
|
||||
var descriptionText: String
|
||||
let itemCount: Int32
|
||||
switch self.selectedSection {
|
||||
case .models:
|
||||
//TODO:localize
|
||||
if component.onlyCrafted {
|
||||
descriptionText = environment.strings.Gift_Variants_CollectionInfo("**\(self.modelCount)** craftable models").string
|
||||
if self.displayCraftableModels {
|
||||
descriptionText = environment.strings.Gift_Variants_CollectionInfo(environment.strings.Gift_Variants_CollectionInfo_CraftableModel(self.craftableModelCount)).string
|
||||
itemCount = self.craftableModelCount
|
||||
descriptionText += "\n[\(environment.strings.Gift_Variants_ViewPrimaryModels) >]()"
|
||||
} else {
|
||||
descriptionText = environment.strings.Gift_Variants_CollectionInfo(environment.strings.Gift_Variants_CollectionInfo_Model(self.modelCount)).string
|
||||
descriptionText = environment.strings.Gift_Variants_CollectionInfo(environment.strings.Gift_Variants_CollectionInfo_Model(self.primaryModelCount)).string
|
||||
itemCount = self.primaryModelCount
|
||||
|
||||
if self.craftableModelCount > 0 {
|
||||
descriptionText += "\n[\(environment.strings.Gift_Variants_ViewCraftableModels) >]()"
|
||||
}
|
||||
}
|
||||
itemCount = self.modelCount
|
||||
case .backdrops:
|
||||
descriptionText = environment.strings.Gift_Variants_CollectionInfo(environment.strings.Gift_Variants_CollectionInfo_Backdrop(self.backdropCount)).string
|
||||
itemCount = self.backdropCount
|
||||
|
|
@ -954,14 +994,45 @@ private final class GiftUpgradeVariantsScreenComponent: Component {
|
|||
let descriptionFont = Font.regular(13.0)
|
||||
let descriptionBoldFont = Font.semibold(13.0)
|
||||
let descriptionTextColor = theme.list.itemSecondaryTextColor
|
||||
let descriptionMarkdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: descriptionFont, textColor: descriptionTextColor), bold: MarkdownAttributeSet(font: descriptionBoldFont, textColor: descriptionTextColor), link: MarkdownAttributeSet(font: descriptionFont, textColor: descriptionTextColor), linkAttribute: { contents in
|
||||
let descriptionLinkColor = theme.list.itemAccentColor
|
||||
let descriptionMarkdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: descriptionFont, textColor: descriptionTextColor), bold: MarkdownAttributeSet(font: descriptionBoldFont, textColor: descriptionTextColor), link: MarkdownAttributeSet(font: descriptionFont, textColor: descriptionLinkColor), linkAttribute: { contents in
|
||||
return (TelegramTextAttributes.URL, contents)
|
||||
})
|
||||
|
||||
if self.cachedSmallChevronImage == nil || self.cachedSmallChevronImage?.1 !== environment.theme {
|
||||
self.cachedSmallChevronImage = (generateTintedImage(image: UIImage(bundleImageName: "Item List/InlineTextRightArrow"), color: descriptionLinkColor)!, theme)
|
||||
}
|
||||
|
||||
let descriptionAttributedString = parseMarkdownIntoAttributedString(descriptionText, attributes: descriptionMarkdownAttributes, textAlignment: .center).mutableCopy() as! NSMutableAttributedString
|
||||
if let range = descriptionAttributedString.string.range(of: ">"), let chevronImage = self.cachedSmallChevronImage?.0 {
|
||||
descriptionAttributedString.addAttribute(.attachment, value: chevronImage, range: NSRange(range, in: descriptionAttributedString.string))
|
||||
}
|
||||
|
||||
let descriptionSize = self.descriptionText.update(
|
||||
transition: .immediate,
|
||||
component: AnyComponent(MultilineTextComponent(
|
||||
text: .markdown(text: descriptionText, attributes: descriptionMarkdownAttributes)
|
||||
text: .plain(descriptionAttributedString),
|
||||
horizontalAlignment: .center,
|
||||
maximumNumberOfLines: 3,
|
||||
lineSpacing: 0.2,
|
||||
highlightColor: descriptionLinkColor.withAlphaComponent(0.1),
|
||||
highlightInset: UIEdgeInsets(top: 0.0, left: 0.0, bottom: 0.0, right: -8.0),
|
||||
highlightAction: { attributes in
|
||||
if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] {
|
||||
return NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
},
|
||||
tapAction: { [weak self] attributes, _ in
|
||||
if let self, let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String {
|
||||
self.displayCraftableModels = !self.displayCraftableModels
|
||||
self.isPlaying = false
|
||||
|
||||
self.updateEffectiveGifts(attributes: component.attributes)
|
||||
self.state?.updated(transition: ComponentTransition(animation: .curve(duration: 0.4, curve: .spring)))
|
||||
}
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 100.0)
|
||||
|
|
@ -973,6 +1044,7 @@ private final class GiftUpgradeVariantsScreenComponent: Component {
|
|||
}
|
||||
descriptionView.frame = descriptionFrame
|
||||
}
|
||||
self.currentDescriptionHeight = descriptionSize.height
|
||||
contentHeight += descriptionSize.height
|
||||
contentHeight += 26.0
|
||||
|
||||
|
|
@ -1061,6 +1133,13 @@ private final class GiftUpgradeVariantsScreenComponent: Component {
|
|||
self.previewTimerTick()
|
||||
}
|
||||
self.state?.updated(transition: .easeInOut(duration: 0.25))
|
||||
|
||||
if let buttonView = self.playbackButton.view {
|
||||
buttonView.isUserInteractionEnabled = false
|
||||
Queue.mainQueue().after(0.3, {
|
||||
buttonView.isUserInteractionEnabled = true
|
||||
})
|
||||
}
|
||||
}
|
||||
)),
|
||||
environment: {},
|
||||
|
|
@ -1156,7 +1235,7 @@ public class GiftUpgradeVariantsScreen: ViewControllerComponentContainer {
|
|||
public init(
|
||||
context: AccountContext,
|
||||
gift: StarGift,
|
||||
onlyCrafted: Bool = false,
|
||||
crafted: Bool = false,
|
||||
attributes: [StarGift.UniqueGift.Attribute],
|
||||
selectedAttributes: [StarGift.UniqueGift.Attribute]?,
|
||||
focusedAttribute: StarGift.UniqueGift.Attribute?
|
||||
|
|
@ -1166,7 +1245,7 @@ public class GiftUpgradeVariantsScreen: ViewControllerComponentContainer {
|
|||
super.init(context: context, component: GiftUpgradeVariantsScreenComponent(
|
||||
context: context,
|
||||
gift: gift,
|
||||
onlyCrafted: onlyCrafted,
|
||||
crafted: crafted,
|
||||
attributes: attributes,
|
||||
selectedAttributes: selectedAttributes,
|
||||
focusedAttribute: focusedAttribute
|
||||
|
|
@ -1194,7 +1273,9 @@ public class GiftUpgradeVariantsScreen: ViewControllerComponentContainer {
|
|||
self.didPlayAppearAnimation = true
|
||||
|
||||
if let componentView = self.node.hostView.componentView as? GiftUpgradeVariantsScreenComponent.View {
|
||||
componentView.animateIn()
|
||||
Queue.mainQueue().justDispatch {
|
||||
componentView.animateIn()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -1352,8 +1433,8 @@ private final class AttributeInfoComponent: Component {
|
|||
}
|
||||
|
||||
var badgeString = ""
|
||||
var badgeColor = component.badgeColor
|
||||
if let rarity {
|
||||
//TODO:localize
|
||||
switch rarity {
|
||||
case let .permille(value):
|
||||
if value == 0 {
|
||||
|
|
@ -1362,11 +1443,17 @@ private final class AttributeInfoComponent: Component {
|
|||
badgeString = formatPercentage(Float(value) * 0.1)
|
||||
}
|
||||
case .epic:
|
||||
badgeString = "epic"
|
||||
badgeString = component.strings.Gift_Attribute_Epic
|
||||
badgeColor = UIColor(rgb: 0xaf52de)
|
||||
case .legendary:
|
||||
badgeString = "legendary"
|
||||
badgeString = component.strings.Gift_Attribute_Legendary
|
||||
badgeColor = UIColor(rgb: 0xd57e32)
|
||||
case .rare:
|
||||
badgeString = "rare"
|
||||
badgeString = component.strings.Gift_Attribute_Rare
|
||||
badgeColor = UIColor(rgb: 0x25a3b9)
|
||||
case .uncommon:
|
||||
badgeString = component.strings.Gift_Attribute_Uncommon
|
||||
badgeColor = UIColor(rgb: 0x22b447)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1406,8 +1493,7 @@ private final class AttributeInfoComponent: Component {
|
|||
|
||||
let badgeBackgroundFrame = badgeFrame.insetBy(dx: -5.5, dy: -2.0)
|
||||
transition.setFrame(layer: self.badgeBackground, frame: badgeBackgroundFrame)
|
||||
transition.setBackgroundColor(layer: self.badgeBackground, color: component.badgeColor)
|
||||
|
||||
transition.setBackgroundColor(layer: self.badgeBackground, color: badgeColor)
|
||||
|
||||
return availableSize
|
||||
}
|
||||
|
|
|
|||
|
|
@ -37,6 +37,8 @@ public func buyStarGiftImpl(
|
|||
parseMarkdown: true
|
||||
)
|
||||
controller.present(alertController, in: .window(.root))
|
||||
|
||||
beforeCompletion()
|
||||
return
|
||||
}
|
||||
|
||||
|
|
@ -207,6 +209,8 @@ public func buyStarGiftImpl(
|
|||
context.sharedContext.applicationBindings.openUrl(fragmentUrl)
|
||||
}
|
||||
))
|
||||
|
||||
beforeCompletion()
|
||||
} else {
|
||||
proceed()
|
||||
}
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -36,6 +36,7 @@ public final class GlassControlPanelComponent: Component {
|
|||
public let rightItem: Item?
|
||||
public let centralItem: Item?
|
||||
public let centerAlignmentIfPossible: Bool
|
||||
public let isDark: Bool?
|
||||
public let tag: AnyObject?
|
||||
|
||||
public init(
|
||||
|
|
@ -44,6 +45,7 @@ public final class GlassControlPanelComponent: Component {
|
|||
centralItem: Item?,
|
||||
rightItem: Item?,
|
||||
centerAlignmentIfPossible: Bool = false,
|
||||
isDark: Bool? = nil,
|
||||
tag: AnyObject? = nil
|
||||
) {
|
||||
self.theme = theme
|
||||
|
|
@ -51,6 +53,7 @@ public final class GlassControlPanelComponent: Component {
|
|||
self.centralItem = centralItem
|
||||
self.rightItem = rightItem
|
||||
self.centerAlignmentIfPossible = centerAlignmentIfPossible
|
||||
self.isDark = isDark
|
||||
self.tag = tag
|
||||
}
|
||||
|
||||
|
|
@ -70,6 +73,9 @@ public final class GlassControlPanelComponent: Component {
|
|||
if lhs.centerAlignmentIfPossible != rhs.centerAlignmentIfPossible {
|
||||
return false
|
||||
}
|
||||
if lhs.isDark != rhs.isDark {
|
||||
return false
|
||||
}
|
||||
if lhs.tag !== rhs.tag {
|
||||
return false
|
||||
}
|
||||
|
|
@ -298,7 +304,7 @@ public final class GlassControlPanelComponent: Component {
|
|||
}
|
||||
|
||||
transition.setFrame(view: self.glassContainerView, frame: CGRect(origin: CGPoint(), size: availableSize))
|
||||
self.glassContainerView.update(size: availableSize, isDark: component.theme.overallDarkAppearance, transition: transition)
|
||||
self.glassContainerView.update(size: availableSize, isDark: component.isDark ?? component.theme.overallDarkAppearance, transition: transition)
|
||||
|
||||
return availableSize
|
||||
}
|
||||
|
|
|
|||
|
|
@ -3734,7 +3734,7 @@ public final class MediaEditorScreenImpl: ViewController, MediaEditorScreen, UID
|
|||
}
|
||||
} else if case let .gift(gift) = subject {
|
||||
isGift = true
|
||||
let media: [Media] = [TelegramMediaAction(action: .starGiftUnique(gift: .unique(gift), isUpgrade: false, isTransferred: false, savedToProfile: false, canExportDate: nil, transferStars: nil, isRefunded: false, isPrepaidUpgrade: false, peerId: nil, senderId: nil, savedId: nil, resaleAmount: nil, canTransferDate: nil, canResaleDate: nil, dropOriginalDetailsStars: nil, assigned: false, fromOffer: false, canCraftAt: nil))]
|
||||
let media: [Media] = [TelegramMediaAction(action: .starGiftUnique(gift: .unique(gift), isUpgrade: false, isTransferred: false, savedToProfile: false, canExportDate: nil, transferStars: nil, isRefunded: false, isPrepaidUpgrade: false, peerId: nil, senderId: nil, savedId: nil, resaleAmount: nil, canTransferDate: nil, canResaleDate: nil, dropOriginalDetailsStars: nil, assigned: false, fromOffer: false, canCraftAt: nil, isCrafted: false))]
|
||||
let message = Message(stableId: 0, stableVersion: 0, id: MessageId(peerId: self.context.account.peerId, namespace: Namespaces.Message.Cloud, id: -1), globallyUniqueId: nil, groupingKey: nil, groupInfo: nil, threadId: nil, timestamp: 0, flags: [], tags: [], globalTags: [], localTags: [], customTags: [], forwardInfo: nil, author: nil, text: "", attributes: [], media: media, peers: SimpleDictionary(), associatedMessages: SimpleDictionary(), associatedMessageIds: [], associatedMedia: [:], associatedThreadInfo: nil, associatedStories: [:])
|
||||
messages = .single([message])
|
||||
} else {
|
||||
|
|
|
|||
|
|
@ -197,6 +197,8 @@ private class MessagePriceItemNode: ListViewItemNode, ItemListItemNode {
|
|||
}
|
||||
|
||||
private let backgroundNode: ASDisplayNode
|
||||
private let highlightNode: ASDisplayNode
|
||||
|
||||
private let topStripeNode: ASDisplayNode
|
||||
private let bottomStripeNode: ASDisplayNode
|
||||
private let maskNode: ASImageNode
|
||||
|
|
@ -225,6 +227,9 @@ private class MessagePriceItemNode: ListViewItemNode, ItemListItemNode {
|
|||
self.backgroundNode = ASDisplayNode()
|
||||
self.backgroundNode.isLayerBacked = true
|
||||
|
||||
self.highlightNode = ASDisplayNode()
|
||||
self.highlightNode.isLayerBacked = true
|
||||
|
||||
self.topStripeNode = ASDisplayNode()
|
||||
self.topStripeNode.isLayerBacked = true
|
||||
|
||||
|
|
@ -297,6 +302,20 @@ private class MessagePriceItemNode: ListViewItemNode, ItemListItemNode {
|
|||
self.item?.openSetCustom?()
|
||||
}
|
||||
|
||||
public func displayHighlight() {
|
||||
if self.backgroundNode.supernode != nil {
|
||||
self.insertSubnode(self.highlightNode, aboveSubnode: self.backgroundNode)
|
||||
} else {
|
||||
self.insertSubnode(self.highlightNode, at: 0)
|
||||
}
|
||||
|
||||
Queue.mainQueue().after(1.2, {
|
||||
self.highlightNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { _ in
|
||||
self.highlightNode.removeFromSupernode()
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func asyncLayout() -> (_ item: MessagePriceItem, _ params: ListViewItemLayoutParams, _ neighbors: ItemListNeighbors) -> (ListViewItemNodeLayout, () -> Void) {
|
||||
let currentItem = self.item
|
||||
|
||||
|
|
@ -329,6 +348,7 @@ private class MessagePriceItemNode: ListViewItemNode, ItemListItemNode {
|
|||
strongSelf.backgroundNode.backgroundColor = item.theme.list.itemBlocksBackgroundColor
|
||||
strongSelf.topStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor
|
||||
strongSelf.bottomStripeNode.backgroundColor = item.theme.list.itemBlocksSeparatorColor
|
||||
strongSelf.highlightNode.backgroundColor = item.theme.list.itemSearchHighlightColor
|
||||
|
||||
if strongSelf.backgroundNode.supernode == nil {
|
||||
strongSelf.insertSubnode(strongSelf.backgroundNode, at: 0)
|
||||
|
|
@ -370,6 +390,7 @@ private class MessagePriceItemNode: ListViewItemNode, ItemListItemNode {
|
|||
strongSelf.maskNode.image = hasCorners ? PresentationResourcesItemList.cornersImage(item.theme, top: hasTopCorners, bottom: hasBottomCorners, glass: item.systemStyle == .glass) : nil
|
||||
|
||||
strongSelf.backgroundNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
|
||||
strongSelf.highlightNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: params.width, height: contentSize.height + min(insets.top, separatorHeight) + min(insets.bottom, separatorHeight)))
|
||||
strongSelf.maskNode.frame = strongSelf.backgroundNode.frame.insetBy(dx: params.leftInset, dy: 0.0)
|
||||
strongSelf.topStripeNode.frame = CGRect(origin: CGPoint(x: 0.0, y: -min(insets.top, separatorHeight)), size: CGSize(width: layoutSize.width, height: separatorHeight))
|
||||
strongSelf.bottomStripeNode.frame = CGRect(origin: CGPoint(x: bottomStripeInset, y: contentSize.height + bottomStripeOffset), size: CGSize(width: layoutSize.width - bottomStripeInset - params.rightInset - separatorRightInset, height: separatorHeight))
|
||||
|
|
|
|||
|
|
@ -587,8 +587,7 @@ public final class PeerInfoCoverComponent: Component {
|
|||
self.avatarPatternContentLayers.append(itemLayer)
|
||||
}
|
||||
|
||||
//itemLayer.frame = itemFrame
|
||||
transition.setFrame(layer: itemLayer, frame: itemFrame)
|
||||
itemLayer.frame = itemFrame
|
||||
itemLayer.layerTintColor = UIColor(white: 0.0, alpha: 0.8).cgColor
|
||||
transition.setAlpha(layer: itemLayer, alpha: 1.0 - itemScaleFraction)
|
||||
|
||||
|
|
|
|||
|
|
@ -89,7 +89,7 @@ private func generateNumberOffsets() -> [CGPoint] {
|
|||
CGPoint(x: 0.9900000000000001, y: 0.0),
|
||||
CGPoint(x: 0.66, y: 0.0),
|
||||
CGPoint(x: 0.5775, y: 0.0),
|
||||
CGPoint(x: 1.2375, y: 0.0),
|
||||
CGPoint(x: 0.7425, y: 0.0),
|
||||
CGPoint(x: 0.9900000000000001, y: 0.0),
|
||||
CGPoint(x: 1.32, y: 0.0),
|
||||
CGPoint(x: 1.155, y: 0.0),
|
||||
|
|
|
|||
|
|
@ -314,7 +314,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||
let tipsPeerDisposable = MetaDisposable()
|
||||
|
||||
let cachedFaq = Promise<ResolvedUrl?>(nil)
|
||||
private var didSetCachedFaq = false
|
||||
var didSetCachedFaq = false
|
||||
|
||||
weak var copyProtectionTooltipController: TooltipController?
|
||||
weak var emojiStatusSelectionController: ViewController?
|
||||
|
|
@ -2370,6 +2370,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||
let controller = GiftViewScreen(
|
||||
context: self.context,
|
||||
subject: .profileGift(self.peerId, gift),
|
||||
profileGiftsContext: profileGifts,
|
||||
updateSavedToProfile: { [weak profileGifts] reference, added in
|
||||
guard let profileGifts else {
|
||||
return
|
||||
|
|
@ -4807,10 +4808,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, PeerInfoScreenNodePro
|
|||
self.headerNode.navigationButtonContainer.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue)
|
||||
|
||||
if self.isSettings {
|
||||
if !self.didSetCachedFaq {
|
||||
self.cachedFaq.set(.single(nil) |> then(cachedFaqInstantPage(context: self.context) |> map(Optional.init)))
|
||||
self.didSetCachedFaq = true
|
||||
}
|
||||
self.setupFaqIfNeeded()
|
||||
|
||||
if let settings = self.data?.globalSettings {
|
||||
self.searchDisplayController = SearchDisplayController(
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import PremiumUI
|
|||
import TelegramPresentationData
|
||||
import PresentationDataUtils
|
||||
import PasswordSetupUI
|
||||
import InstantPageCache
|
||||
|
||||
extension PeerInfoScreenNode {
|
||||
func openSettings(section: PeerInfoSettingsSection) {
|
||||
|
|
@ -298,7 +299,16 @@ extension PeerInfoScreenNode {
|
|||
}
|
||||
}
|
||||
|
||||
func setupFaqIfNeeded() {
|
||||
if !self.didSetCachedFaq {
|
||||
self.cachedFaq.set(.single(nil) |> then(cachedFaqInstantPage(context: self.context) |> map(Optional.init)))
|
||||
self.didSetCachedFaq = true
|
||||
}
|
||||
}
|
||||
|
||||
func openFaq(anchor: String? = nil) {
|
||||
self.setupFaqIfNeeded()
|
||||
|
||||
let presentationData = self.presentationData
|
||||
let progressSignal = Signal<Never, NoError> { [weak self] subscriber in
|
||||
let controller = OverlayStatusController(theme: presentationData.theme, type: .loading(cancelled: nil))
|
||||
|
|
@ -314,6 +324,7 @@ extension PeerInfoScreenNode {
|
|||
let progressDisposable = progressSignal.start()
|
||||
|
||||
let _ = (self.cachedFaq.get()
|
||||
|> filter { $0 != nil }
|
||||
|> take(1)
|
||||
|> deliverOnMainQueue).start(next: { [weak self] resolvedUrl in
|
||||
progressDisposable.dispose()
|
||||
|
|
|
|||
|
|
@ -606,6 +606,7 @@ final class GiftsListView: UIView {
|
|||
subject: .profileGift(self.peerId, product),
|
||||
allSubjects: allSubjects,
|
||||
index: index,
|
||||
profileGiftsContext: self.profileGifts,
|
||||
updateSavedToProfile: { [weak self] reference, added in
|
||||
guard let self else {
|
||||
return
|
||||
|
|
|
|||
|
|
@ -108,6 +108,7 @@ public final class PlainButtonComponent: Component {
|
|||
super.init(frame: frame)
|
||||
|
||||
self.isExclusiveTouch = true
|
||||
self.layer.rasterizationScale = UIScreenScale
|
||||
|
||||
self.contentContainer.isUserInteractionEnabled = false
|
||||
self.addSubview(self.contentContainer)
|
||||
|
|
@ -130,8 +131,13 @@ public final class PlainButtonComponent: Component {
|
|||
self.contentContainer.alpha = 0.7
|
||||
}
|
||||
if animateScale {
|
||||
self.layer.shouldRasterize = true
|
||||
let transition = ComponentTransition(animation: .curve(duration: 0.2, curve: .easeInOut))
|
||||
transition.setScale(layer: self.contentContainer.layer, scale: topScale)
|
||||
transition.setScale(layer: self.contentContainer.layer, scale: topScale, completion: { finished in
|
||||
if finished {
|
||||
self.layer.shouldRasterize = false
|
||||
}
|
||||
})
|
||||
}
|
||||
} else {
|
||||
if animateAlpha {
|
||||
|
|
@ -140,15 +146,21 @@ public final class PlainButtonComponent: Component {
|
|||
}
|
||||
|
||||
if animateScale {
|
||||
self.layer.shouldRasterize = true
|
||||
|
||||
let transition = ComponentTransition(animation: .none)
|
||||
transition.setScale(layer: self.contentContainer.layer, scale: 1.0)
|
||||
|
||||
|
||||
self.contentContainer.layer.animateScale(from: topScale, to: maxScale, duration: 0.13, timingFunction: CAMediaTimingFunctionName.easeOut.rawValue, removeOnCompletion: false, completion: { [weak self] _ in
|
||||
guard let self else {
|
||||
return
|
||||
}
|
||||
|
||||
self.contentContainer.layer.animateScale(from: maxScale, to: 1.0, duration: 0.1, timingFunction: CAMediaTimingFunctionName.easeIn.rawValue)
|
||||
self.contentContainer.layer.animateScale(from: maxScale, to: 1.0, duration: 0.1, timingFunction: CAMediaTimingFunctionName.easeIn.rawValue, completion: { finished in
|
||||
if finished {
|
||||
self.layer.shouldRasterize = false
|
||||
}
|
||||
})
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -195,15 +195,14 @@ private final class ProxyServerPreviewSheetContent: CombinedComponent {
|
|||
commit()
|
||||
return
|
||||
}
|
||||
//TODO:localize
|
||||
let presentationData = self.context.sharedContext.currentPresentationData.with { $0 }
|
||||
let alertController = textAlertController(
|
||||
context: context,
|
||||
title: "Warning",
|
||||
text: "This action will expose your IP address to the admin of the proxy server.",
|
||||
title: presentationData.strings.SocksProxySetup_Warning_Title,
|
||||
text: presentationData.strings.SocksProxySetup_Warning_Text,
|
||||
actions: [
|
||||
TextAlertAction(type: .genericAction, title: presentationData.strings.Common_Cancel, action: {}),
|
||||
TextAlertAction(type: .defaultAction, title: "Proceed", action: {
|
||||
TextAlertAction(type: .defaultAction, title: presentationData.strings.SocksProxySetup_Warning_Proceed, action: {
|
||||
commit()
|
||||
})
|
||||
]
|
||||
|
|
@ -253,14 +252,11 @@ private final class ProxyServerPreviewSheetContent: CombinedComponent {
|
|||
availableSize: CGSize(width: 44.0, height: 44.0),
|
||||
transition: .immediate
|
||||
)
|
||||
|
||||
let titleText: String = "Proxy"
|
||||
let buttonText: String = "Connect Proxy"
|
||||
|
||||
|
||||
let title = title.update(
|
||||
component: MultilineTextComponent(
|
||||
text: .plain(NSAttributedString(
|
||||
string: titleText,
|
||||
string: strings.SocksProxySetup_Title,
|
||||
font: Font.semibold(17.0),
|
||||
textColor: theme.actionSheet.primaryTextColor,
|
||||
paragraphAlignment: .center
|
||||
|
|
@ -323,7 +319,7 @@ private final class ProxyServerPreviewSheetContent: CombinedComponent {
|
|||
))
|
||||
}
|
||||
|
||||
var statusText = "Check Status"
|
||||
var statusText = strings.SocksProxySetup_CheckStatus
|
||||
var statusColor = tableLinkColor
|
||||
var statusIsActive = true
|
||||
if let status = state.status {
|
||||
|
|
@ -378,7 +374,7 @@ private final class ProxyServerPreviewSheetContent: CombinedComponent {
|
|||
),
|
||||
content: AnyComponentWithIdentity(
|
||||
id: AnyHashable(0),
|
||||
component: AnyComponent(MultilineTextComponent(text: .plain(NSMutableAttributedString(string: buttonText, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center))))
|
||||
component: AnyComponent(MultilineTextComponent(text: .plain(NSMutableAttributedString(string: strings.SocksProxySetup_ConnectAndSave, font: Font.semibold(17.0), textColor: theme.list.itemCheckColors.foregroundColor, paragraphAlignment: .center))))
|
||||
),
|
||||
displaysProgress: state.inProgress,
|
||||
action: {
|
||||
|
|
|
|||
|
|
@ -22,6 +22,7 @@ swift_library(
|
|||
"//submodules/Components/SheetComponent",
|
||||
"//submodules/TelegramUI/Components/ButtonComponent",
|
||||
"//submodules/TelegramUI/Components/LottieComponent",
|
||||
"//submodules/TelegramUI/Components/GlassBarButtonComponent",
|
||||
],
|
||||
visibility = [
|
||||
"//visibility:public",
|
||||
|
|
|
|||
|
|
@ -1 +0,0 @@
|
|||
|
||||
|
|
@ -12,6 +12,7 @@ import BalancedTextComponent
|
|||
import Markdown
|
||||
import TelegramStringFormatting
|
||||
import BundleIconComponent
|
||||
import GlassBarButtonComponent
|
||||
|
||||
public final class ButtonSubtitleComponent: CombinedComponent {
|
||||
public let title: String
|
||||
|
|
@ -124,23 +125,29 @@ private final class StoryQualityUpgradeSheetContentComponent: Component {
|
|||
}
|
||||
let cancelButtonSize = cancelButton.update(
|
||||
transition: transition,
|
||||
component: AnyComponent(Button(
|
||||
content: AnyComponent(Text(text: environment.strings.Common_Cancel, font: Font.regular(17.0), color: environment.theme.list.itemAccentColor)),
|
||||
action: { [weak self] in
|
||||
guard let self, let component = self.component else {
|
||||
return
|
||||
}
|
||||
component: AnyComponent(GlassBarButtonComponent(
|
||||
size: CGSize(width: 44.0, height: 44.0),
|
||||
backgroundColor: nil,
|
||||
isDark: environment.theme.overallDarkAppearance,
|
||||
state: .glass,
|
||||
component: AnyComponentWithIdentity(id: "close", component: AnyComponent(
|
||||
BundleIconComponent(
|
||||
name: "Navigation/Close",
|
||||
tintColor: environment.theme.chat.inputPanel.panelControlColor
|
||||
)
|
||||
)),
|
||||
action: { _ in
|
||||
component.dismiss()
|
||||
}
|
||||
).minSize(CGSize(width: 8.0, height: 44.0))),
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 200.0, height: 100.0)
|
||||
containerSize: CGSize(width: 44.0, height: 44.0)
|
||||
)
|
||||
if let cancelButtonView = cancelButton.view {
|
||||
if cancelButtonView.superview == nil {
|
||||
self.addSubview(cancelButtonView)
|
||||
}
|
||||
transition.setFrame(view: cancelButtonView, frame: CGRect(origin: CGPoint(x: 16.0, y: 6.0), size: cancelButtonSize))
|
||||
transition.setFrame(view: cancelButtonView, frame: CGRect(origin: CGPoint(x: 16.0, y: 16.0), size: cancelButtonSize))
|
||||
}
|
||||
|
||||
var contentHeight: CGFloat = 0.0
|
||||
|
|
@ -152,7 +159,8 @@ private final class StoryQualityUpgradeSheetContentComponent: Component {
|
|||
content: LottieComponent.AppBundleContent(name: "StoryUpgradeSheet"),
|
||||
color: nil,
|
||||
startingPosition: .begin,
|
||||
size: CGSize(width: 100.0, height: 100.0)
|
||||
size: CGSize(width: 100.0, height: 100.0),
|
||||
loop: true
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: 100.0, height: 100.0)
|
||||
|
|
@ -217,16 +225,18 @@ private final class StoryQualityUpgradeSheetContentComponent: Component {
|
|||
color: environment.theme.list.itemCheckColors.foregroundColor.withMultipliedAlpha(0.7)
|
||||
))))
|
||||
|
||||
let buttonInsets = ContainerViewLayout.concentricInsets(bottomInset: environment.safeInsets.bottom, innerDiameter: 52.0, sideInset: 30.0)
|
||||
let buttonSize = self.button.update(
|
||||
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)
|
||||
),
|
||||
content: AnyComponentWithIdentity(id: AnyHashable(0 as Int), component: AnyComponent(
|
||||
VStack(buttonContents, spacing: 3.0)
|
||||
VStack(buttonContents, spacing: 1.0)
|
||||
)),
|
||||
isEnabled: true,
|
||||
allowActionWhenDisabled: true,
|
||||
|
|
@ -240,9 +250,9 @@ private final class StoryQualityUpgradeSheetContentComponent: Component {
|
|||
}
|
||||
)),
|
||||
environment: {},
|
||||
containerSize: CGSize(width: availableSize.width - sideInset * 2.0, height: 50.0)
|
||||
containerSize: CGSize(width: availableSize.width - buttonInsets.left - buttonInsets.right, height: 52.0)
|
||||
)
|
||||
let buttonFrame = CGRect(origin: CGPoint(x: sideInset, y: contentHeight), size: buttonSize)
|
||||
let buttonFrame = CGRect(origin: CGPoint(x: buttonInsets.left, y: contentHeight), size: buttonSize)
|
||||
if let buttonView = self.button.view {
|
||||
if buttonView.superview == nil {
|
||||
self.addSubview(buttonView)
|
||||
|
|
@ -250,12 +260,7 @@ private final class StoryQualityUpgradeSheetContentComponent: Component {
|
|||
transition.setFrame(view: buttonView, frame: buttonFrame)
|
||||
}
|
||||
contentHeight += buttonSize.height
|
||||
|
||||
if environment.safeInsets.bottom.isZero {
|
||||
contentHeight += 16.0
|
||||
} else {
|
||||
contentHeight += environment.safeInsets.bottom + 14.0
|
||||
}
|
||||
contentHeight += buttonInsets.bottom
|
||||
|
||||
return CGSize(width: availableSize.width, height: contentHeight)
|
||||
}
|
||||
|
|
@ -355,7 +360,8 @@ private final class StoryQualityUpgradeSheetScreenComponent: Component {
|
|||
})
|
||||
}
|
||||
)),
|
||||
backgroundColor: .color(environment.theme.overallDarkAppearance ? environment.theme.list.itemBlocksBackgroundColor : environment.theme.list.blocksBackgroundColor),
|
||||
style: .glass,
|
||||
backgroundColor: .color(environment.theme.list.modalPlainBackgroundColor),
|
||||
animateOut: self.sheetAnimateOut
|
||||
)),
|
||||
environment: {
|
||||
|
|
|
|||
|
|
@ -589,7 +589,7 @@ public final class TextFieldComponent: Component {
|
|||
}
|
||||
}
|
||||
|
||||
public func chatInputTextNodeShouldReturn() -> Bool {
|
||||
public func chatInputTextNodeShouldReturn(modifierFlags: UIKeyModifierFlags) -> Bool {
|
||||
guard let component = self.component else {
|
||||
return true
|
||||
}
|
||||
|
|
|
|||
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Stars.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Stars.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "star (3).pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Stars.imageset/star (3).pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Stars.imageset/star (3).pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Ton.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Ton.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "ton (3).pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Ton.imageset/ton (3).pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Chat/Context Menu/Ton.imageset/ton (3).pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Premium/Craft/Chance.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Premium/Craft/Chance.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "puzzle.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
submodules/TelegramUI/Images.xcassets/Premium/Craft/Chance.imageset/puzzle.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Premium/Craft/Chance.imageset/puzzle.pdf
vendored
Normal file
Binary file not shown.
|
|
@ -0,0 +1,9 @@
|
|||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
},
|
||||
"properties" : {
|
||||
"provides-namespace" : true
|
||||
}
|
||||
}
|
||||
12
submodules/TelegramUI/Images.xcassets/Premium/Craft/Rare.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Premium/Craft/Rare.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "diamond (2).pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
submodules/TelegramUI/Images.xcassets/Premium/Craft/Rare.imageset/diamond (2).pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Premium/Craft/Rare.imageset/diamond (2).pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Premium/Craft/Result.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Premium/Craft/Result.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "chance.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
submodules/TelegramUI/Images.xcassets/Premium/Craft/Result.imageset/chance.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Premium/Craft/Result.imageset/chance.pdf
vendored
Normal file
Binary file not shown.
12
submodules/TelegramUI/Images.xcassets/Premium/GiftCrash.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Premium/GiftCrash.imageset/Contents.json
vendored
Normal file
|
|
@ -0,0 +1,12 @@
|
|||
{
|
||||
"images" : [
|
||||
{
|
||||
"filename" : "giftcrash.pdf",
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
BIN
submodules/TelegramUI/Images.xcassets/Premium/GiftCrash.imageset/giftcrash.pdf
vendored
Normal file
BIN
submodules/TelegramUI/Images.xcassets/Premium/GiftCrash.imageset/giftcrash.pdf
vendored
Normal file
Binary file not shown.
|
|
@ -15,16 +15,15 @@ import ChatControllerInteraction
|
|||
|
||||
extension ChatControllerImpl {
|
||||
var keyShortcutsInternal: [KeyShortcut] {
|
||||
if !self.traceVisibility() || !isTopmostChatController(self) {
|
||||
if !isTopmostChatController(self) {
|
||||
return []
|
||||
}
|
||||
|
||||
let strings = self.presentationData.strings
|
||||
|
||||
var inputShortcuts: [KeyShortcut]
|
||||
var inputShortcuts: [KeyShortcut] = []
|
||||
if self.chatDisplayNode.isInputViewFocused {
|
||||
inputShortcuts = [
|
||||
KeyShortcut(title: strings.KeyCommand_SendMessage, input: "\r", action: {}),
|
||||
KeyShortcut(input: "B", modifiers: [.command], action: { [weak self] in
|
||||
if let strongSelf = self {
|
||||
strongSelf.interfaceInteraction?.updateTextInputStateAndMode { current, inputMode in
|
||||
|
|
@ -80,6 +79,16 @@ extension ChatControllerImpl {
|
|||
}
|
||||
})
|
||||
]
|
||||
|
||||
if self.context.sharedContext.currentChatSettings.with({ $0 }).sendWithCmdEnter {
|
||||
inputShortcuts.append(
|
||||
KeyShortcut(title: strings.KeyCommand_SendMessage, input: "\r", modifiers: [.command], action: {})
|
||||
)
|
||||
} else {
|
||||
inputShortcuts.append(
|
||||
KeyShortcut(title: strings.KeyCommand_SendMessage, input: "\r", action: {})
|
||||
)
|
||||
}
|
||||
} else if UIResponder.currentFirst() == nil {
|
||||
inputShortcuts = [
|
||||
KeyShortcut(title: strings.KeyCommand_FocusOnInputField, input: "\r", action: { [weak self] in
|
||||
|
|
|
|||
|
|
@ -182,7 +182,7 @@ func textInputAccessoryPanel(
|
|||
|
||||
let alertController = AlertScreen(
|
||||
context: context,
|
||||
configuration: AlertScreen.Configuration(actionAlignment: .vertical),
|
||||
configuration: AlertScreen.Configuration(actionAlignment: .vertical, allowInputInset: true),
|
||||
content: content,
|
||||
actions: [
|
||||
.init(title: strings.Conversation_ForwardOptions_ShowOptions, action: {
|
||||
|
|
|
|||
|
|
@ -1136,7 +1136,7 @@ func contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState
|
|||
let sendGiftTitle: String
|
||||
var isIncoming = message.effectivelyIncoming(context.account.peerId)
|
||||
for media in message.media {
|
||||
if let action = media as? TelegramMediaAction, case let .starGiftUnique(_, isUpgrade, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = action.action {
|
||||
if let action = media as? TelegramMediaAction, case let .starGiftUnique(_, isUpgrade, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _, _) = action.action {
|
||||
if isUpgrade && message.author?.id == context.account.peerId {
|
||||
isIncoming = true
|
||||
}
|
||||
|
|
|
|||
|
|
@ -255,6 +255,9 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||
public let currentMediaDisplaySettings: Atomic<MediaDisplaySettings>
|
||||
private var mediaDisplaySettingsDisposable: Disposable?
|
||||
|
||||
public let currentChatSettings: Atomic<ChatSettings>
|
||||
private var chatSettingsDisposable: Disposable?
|
||||
|
||||
public let currentStickerSettings: Atomic<StickerSettings>
|
||||
private var stickerSettingsDisposable: Disposable?
|
||||
|
||||
|
|
@ -339,6 +342,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||
self.currentMediaDisplaySettings = Atomic(value: initialPresentationDataAndSettings.mediaDisplaySettings)
|
||||
self.currentStickerSettings = Atomic(value: initialPresentationDataAndSettings.stickerSettings)
|
||||
self.currentInAppNotificationSettings = Atomic(value: initialPresentationDataAndSettings.inAppNotificationSettings)
|
||||
self.currentChatSettings = Atomic(value: initialPresentationDataAndSettings.chatSettings)
|
||||
|
||||
if automaticEnergyUsageShouldBeOnNow(settings: self.currentAutomaticMediaDownloadSettings) {
|
||||
self.energyUsageSettings = EnergyUsageSettings.powerSavingDefault
|
||||
|
|
@ -485,6 +489,15 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||
}
|
||||
})
|
||||
|
||||
self.chatSettingsDisposable = (self.accountManager.sharedData(keys: [ApplicationSpecificSharedDataKeys.chatSettings])
|
||||
|> deliverOnMainQueue).start(next: { [weak self] sharedData in
|
||||
if let strongSelf = self {
|
||||
if let settings = sharedData.entries[ApplicationSpecificSharedDataKeys.chatSettings]?.get(ChatSettings.self) {
|
||||
let _ = strongSelf.currentChatSettings.swap(settings)
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
let immediateExperimentalUISettingsValue = self.immediateExperimentalUISettingsValue
|
||||
let _ = immediateExperimentalUISettingsValue.swap(initialPresentationDataAndSettings.experimentalUISettings)
|
||||
|
||||
|
|
@ -1078,6 +1091,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||
self.inAppNotificationSettingsDisposable?.dispose()
|
||||
self.mediaInputSettingsDisposable?.dispose()
|
||||
self.mediaDisplaySettingsDisposable?.dispose()
|
||||
self.chatSettingsDisposable?.dispose()
|
||||
self.stickerSettingsDisposable?.dispose()
|
||||
self.callDisposable?.dispose()
|
||||
self.groupCallDisposable?.dispose()
|
||||
self.callStateDisposable?.dispose()
|
||||
|
|
@ -3852,8 +3867,8 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||
return GiftViewScreen(context: context, subject: .wearPreview(gift, attributes))
|
||||
}
|
||||
|
||||
public func makeGiftUpgradePreviewScreen(context: AccountContext, attributes: [StarGift.UniqueGift.Attribute], peerName: String) -> ViewController {
|
||||
return GiftViewScreen(context: context, subject: .upgradePreview(attributes, peerName))
|
||||
public func makeGiftUpgradePreviewScreen(context: AccountContext, gift: StarGift.Gift, attributes: [StarGift.UniqueGift.Attribute], peerName: String) -> ViewController {
|
||||
return GiftViewScreen(context: context, subject: .upgradePreview(gift, attributes, peerName))
|
||||
}
|
||||
|
||||
public func makeGiftAuctionInfoScreen(context: AccountContext, auctionContext: GiftAuctionContext, completion: (() -> Void)?) -> ViewController {
|
||||
|
|
@ -3876,16 +3891,16 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||
return giftOfferAlertController(context: context, updatedPresentationData: updatedPresentationData, gift: gift, peer: peer, amount: amount, commit: commit)
|
||||
}
|
||||
|
||||
public func makeGiftUpgradeVariantsScreen(context: AccountContext, gift: StarGift, onlyCrafted: Bool, attributes: [StarGift.UniqueGift.Attribute], selectedAttributes: [StarGift.UniqueGift.Attribute]?, focusedAttribute: StarGift.UniqueGift.Attribute?) -> ViewController {
|
||||
return GiftUpgradeVariantsScreen(context: context, gift: gift, onlyCrafted: onlyCrafted, attributes: attributes, selectedAttributes: selectedAttributes, focusedAttribute: focusedAttribute)
|
||||
public func makeGiftUpgradeVariantsScreen(context: AccountContext, gift: StarGift, crafted: Bool, attributes: [StarGift.UniqueGift.Attribute], selectedAttributes: [StarGift.UniqueGift.Attribute]?, focusedAttribute: StarGift.UniqueGift.Attribute?) -> ViewController {
|
||||
return GiftUpgradeVariantsScreen(context: context, gift: gift, crafted: crafted, attributes: attributes, selectedAttributes: selectedAttributes, focusedAttribute: focusedAttribute)
|
||||
}
|
||||
|
||||
public func makeGiftAuctionWearPreviewScreen(context: AccountContext, auctionContext: GiftAuctionContext, acquiredGifts: Signal<[GiftAuctionAcquiredGift], NoError>?, attributes: [StarGift.UniqueGift.Attribute], completion: @escaping () -> Void) -> ViewController {
|
||||
return GiftAuctionWearPreviewScreen(context: context, auctionContext: auctionContext, attributes: attributes, completion: completion)
|
||||
}
|
||||
|
||||
public func makeGiftCraftScreen(context: AccountContext, gift: StarGift.UniqueGift) -> ViewController {
|
||||
return GiftCraftScreen(context: context, gift: gift)
|
||||
public func makeGiftCraftScreen(context: AccountContext, gift: StarGift.UniqueGift, profileGiftsContext: ProfileGiftsContext?) -> ViewController {
|
||||
return GiftCraftScreen(context: context, gift: gift, profileGiftsContext: profileGiftsContext)
|
||||
}
|
||||
|
||||
public func makeGiftDemoScreen(context: AccountContext) -> ViewController {
|
||||
|
|
|
|||
49
submodules/TelegramUIPreferences/Sources/ChatSettings.swift
Normal file
49
submodules/TelegramUIPreferences/Sources/ChatSettings.swift
Normal file
|
|
@ -0,0 +1,49 @@
|
|||
import Foundation
|
||||
import TelegramCore
|
||||
import SwiftSignalKit
|
||||
|
||||
public struct ChatSettings: Codable, Equatable {
|
||||
public let sendWithCmdEnter: Bool
|
||||
|
||||
public static var defaultSettings: ChatSettings {
|
||||
return ChatSettings(sendWithCmdEnter: false)
|
||||
}
|
||||
|
||||
public init(sendWithCmdEnter: Bool) {
|
||||
self.sendWithCmdEnter = sendWithCmdEnter
|
||||
}
|
||||
|
||||
public init(from decoder: Decoder) throws {
|
||||
let container = try decoder.container(keyedBy: StringCodingKey.self)
|
||||
|
||||
self.sendWithCmdEnter = (try container.decode(Int32.self, forKey: "sendWithCmdEnter")) != 0
|
||||
}
|
||||
|
||||
public func encode(to encoder: Encoder) throws {
|
||||
var container = encoder.container(keyedBy: StringCodingKey.self)
|
||||
|
||||
try container.encode((self.sendWithCmdEnter ? 1 : 0) as Int32, forKey: "sendWithCmdEnter")
|
||||
}
|
||||
|
||||
public static func ==(lhs: ChatSettings, rhs: ChatSettings) -> Bool {
|
||||
return lhs.sendWithCmdEnter == rhs.sendWithCmdEnter
|
||||
}
|
||||
|
||||
public func withUpdatedSendWithCmdEnter(_ sendWithCmdEnter: Bool) -> ChatSettings {
|
||||
return ChatSettings(sendWithCmdEnter: sendWithCmdEnter)
|
||||
}
|
||||
}
|
||||
|
||||
public func updateChatSettingsInteractively(accountManager: AccountManager<TelegramAccountManagerTypes>, _ f: @escaping (ChatSettings) -> ChatSettings) -> Signal<Void, NoError> {
|
||||
return accountManager.transaction { transaction -> Void in
|
||||
transaction.updateSharedData(ApplicationSpecificSharedDataKeys.chatSettings, { entry in
|
||||
let currentSettings: ChatSettings
|
||||
if let entry = entry?.get(ChatSettings.self) {
|
||||
currentSettings = entry
|
||||
} else {
|
||||
currentSettings = ChatSettings.defaultSettings
|
||||
}
|
||||
return SharedPreferencesEntry(f(currentSettings))
|
||||
})
|
||||
}
|
||||
}
|
||||
|
|
@ -43,6 +43,7 @@ private enum ApplicationSpecificSharedDataKeyValues: Int32 {
|
|||
case drawingSettings = 19
|
||||
case mediaDisplaySettings = 20
|
||||
case updateSettings = 21
|
||||
case chatSettings = 22
|
||||
}
|
||||
|
||||
public struct ApplicationSpecificSharedDataKeys {
|
||||
|
|
@ -68,6 +69,7 @@ public struct ApplicationSpecificSharedDataKeys {
|
|||
public static let drawingSettings = applicationSpecificPreferencesKey(ApplicationSpecificSharedDataKeyValues.drawingSettings.rawValue)
|
||||
public static let mediaDisplaySettings = applicationSpecificPreferencesKey(ApplicationSpecificSharedDataKeyValues.mediaDisplaySettings.rawValue)
|
||||
public static let updateSettings = applicationSpecificPreferencesKey(ApplicationSpecificSharedDataKeyValues.updateSettings.rawValue)
|
||||
public static let chatSettings = applicationSpecificPreferencesKey(ApplicationSpecificSharedDataKeyValues.chatSettings.rawValue)
|
||||
}
|
||||
|
||||
private enum ApplicationSpecificItemCacheCollectionIdValues: Int8 {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue