Various Improvements

This commit is contained in:
Ilya Laktyushin 2022-04-27 00:55:00 +04:00
parent 474b918bd2
commit 7b95219e14
45 changed files with 1742 additions and 555 deletions

View File

@ -7536,3 +7536,9 @@ Sorry for the inconvenience.";
"Channel.AddUserKickedError" = "Sorry, you can't add this user because they are on the list of Removed Users and you can't unban them.";
"Channel.AddAdminKickedError" = "Sorry, you can't add this user as an admin because they are in the Removed Users list and you can't unban them.";
"Premium.Stickers.Description" = "Unlock this sticker and more by subscribing to Telegram Premium.";
"Premium.Stickers.Proceed" = "Unlock Premium Stickers";
"Premium.Reactions.Description" = "Unlock additional reactions by subscribing to Telegram Premium.";
"Premium.Reactions.Proceed" = "Unlock Additional Reactions";

View File

@ -55,21 +55,6 @@ private class AvatarNodeParameters: NSObject {
}
}
private func generateGradientFilledCircleImage(diameter: CGFloat, colors: NSArray) -> UIImage? {
return generateImage(CGSize(width: diameter, height: diameter), contextGenerator: { size, context in
let bounds = CGRect(origin: CGPoint(), size: size)
context.clear(bounds)
context.addEllipse(in: bounds)
context.clip()
var locations: [CGFloat] = [0.0, 1.0]
let colorSpace = CGColorSpaceCreateDeviceRGB()
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors, locations: &locations)!
context.drawLinearGradient(gradient, start: CGPoint(), end: CGPoint(x: 0.0, y: bounds.size.height), options: CGGradientDrawingOptions())
})
}
private let grayscaleColors: NSArray = [
UIColor(rgb: 0xb1b1b1).cgColor, UIColor(rgb: 0xcdcdcd).cgColor
]

View File

@ -22,6 +22,7 @@ public final class PeekControllerGestureRecognizer: UIPanGestureRecognizer {
private let present: (PeekControllerContent, ASDisplayNode) -> ViewController?
private let updateContent: (PeekControllerContent?) -> Void
private let activateBySingleTap: Bool
public var checkSingleTapActivationAtPoint: ((CGPoint) -> Bool)?
private var tapLocation: CGPoint?
private var longTapTimer: SwiftSignalKit.Timer?
@ -129,7 +130,12 @@ public final class PeekControllerGestureRecognizer: UIPanGestureRecognizer {
override public func touchesEnded(_ touches: Set<UITouch>, with event: UIEvent) {
super.touchesEnded(touches, with: event)
if self.activateBySingleTap, self.presentedController == nil {
var activateBySingleTap = self.activateBySingleTap
if !activateBySingleTap, let checkSingleTapActivationAtPoint = self.checkSingleTapActivationAtPoint, let tapLocation = self.tapLocation {
activateBySingleTap = checkSingleTapActivationAtPoint(tapLocation)
}
if activateBySingleTap, self.presentedController == nil {
self.longTapTimer?.invalidate()
self.pressTimer?.invalidate()
if let tapLocation = self.tapLocation {

View File

@ -354,6 +354,21 @@ public func generateGradientImage(size: CGSize, colors: [UIColor], locations: [C
return image
}
public func generateGradientFilledCircleImage(diameter: CGFloat, colors: NSArray) -> UIImage? {
return generateImage(CGSize(width: diameter, height: diameter), contextGenerator: { size, context in
let bounds = CGRect(origin: CGPoint(), size: size)
context.clear(bounds)
context.addEllipse(in: bounds)
context.clip()
var locations: [CGFloat] = [0.0, 1.0]
let colorSpace = CGColorSpaceCreateDeviceRGB()
let gradient = CGGradient(colorsSpace: colorSpace, colors: colors, locations: &locations)!
context.drawLinearGradient(gradient, start: CGPoint(), end: CGPoint(x: 0.0, y: bounds.size.height), options: CGGradientDrawingOptions())
})
}
public func generateScaledImage(image: UIImage?, size: CGSize, opaque: Bool = true, scale: CGFloat? = nil) -> UIImage? {
guard let image = image else {
return nil

View File

@ -18,6 +18,7 @@ public class ImmediateTextNode: TextNode {
public var textStroke: (UIColor, CGFloat)?
public var cutout: TextNodeCutout?
public var displaySpoilers = false
public var countIfMoreThanOneLineOverMaximum = false
public var truncationMode: NSLineBreakMode {
get {
@ -90,7 +91,7 @@ public class ImmediateTextNode: TextNode {
self.constrainedSize = constrainedSize
let makeLayout = TextNode.asyncLayout(self)
let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, verticalAlignment: self.verticalAlignment, lineSpacing: self.lineSpacing, cutout: self.cutout, insets: self.insets, textShadowColor: self.textShadowColor, textStroke: self.textStroke, displaySpoilers: self.displaySpoilers))
let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, verticalAlignment: self.verticalAlignment, lineSpacing: self.lineSpacing, cutout: self.cutout, insets: self.insets, textShadowColor: self.textShadowColor, textStroke: self.textStroke, displaySpoilers: self.displaySpoilers, countIfMoreThanOneLineOverMaximum: self.countIfMoreThanOneLineOverMaximum))
let _ = apply()
if layout.numberOfLines > 1 {
self.trailingLineWidth = layout.trailingLineWidth
@ -104,7 +105,7 @@ public class ImmediateTextNode: TextNode {
self.constrainedSize = constrainedSize
let makeLayout = TextNode.asyncLayout(self)
let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, verticalAlignment: self.verticalAlignment, lineSpacing: self.lineSpacing, cutout: self.cutout, insets: self.insets, displaySpoilers: self.displaySpoilers))
let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, verticalAlignment: self.verticalAlignment, lineSpacing: self.lineSpacing, cutout: self.cutout, insets: self.insets, displaySpoilers: self.displaySpoilers, countIfMoreThanOneLineOverMaximum: self.countIfMoreThanOneLineOverMaximum))
let _ = apply()
return ImmediateTextNodeLayoutInfo(size: layout.size, truncated: layout.truncated)
}
@ -113,7 +114,7 @@ public class ImmediateTextNode: TextNode {
self.constrainedSize = constrainedSize
let makeLayout = TextNode.asyncLayout(self)
let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, verticalAlignment: self.verticalAlignment, lineSpacing: self.lineSpacing, cutout: self.cutout, insets: self.insets, displaySpoilers: self.displaySpoilers))
let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, verticalAlignment: self.verticalAlignment, lineSpacing: self.lineSpacing, cutout: self.cutout, insets: self.insets, displaySpoilers: self.displaySpoilers, countIfMoreThanOneLineOverMaximum: self.countIfMoreThanOneLineOverMaximum))
let _ = apply()
return layout
}
@ -237,7 +238,7 @@ public class ImmediateTextView: TextView {
public var textStroke: (UIColor, CGFloat)?
public var cutout: TextNodeCutout?
public var displaySpoilers = false
public var truncationMode: NSLineBreakMode {
get {
switch self.truncationType {
@ -286,7 +287,7 @@ public class ImmediateTextView: TextView {
self.constrainedSize = constrainedSize
let makeLayout = TextView.asyncLayout(self)
let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, verticalAlignment: self.verticalAlignment, lineSpacing: self.lineSpacing, cutout: self.cutout, insets: self.insets, textShadowColor: self.textShadowColor, textStroke: self.textStroke, displaySpoilers: self.displaySpoilers))
let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, verticalAlignment: self.verticalAlignment, lineSpacing: self.lineSpacing, cutout: self.cutout, insets: self.insets, textShadowColor: self.textShadowColor, textStroke: self.textStroke, displaySpoilers: self.displaySpoilers, countIfMoreThanOneLineOverMaximum: false))
let _ = apply()
if layout.numberOfLines > 1 {
self.trailingLineWidth = layout.trailingLineWidth
@ -300,7 +301,7 @@ public class ImmediateTextView: TextView {
self.constrainedSize = constrainedSize
let makeLayout = TextView.asyncLayout(self)
let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, verticalAlignment: self.verticalAlignment, lineSpacing: self.lineSpacing, cutout: self.cutout, insets: self.insets, displaySpoilers: self.displaySpoilers))
let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, verticalAlignment: self.verticalAlignment, lineSpacing: self.lineSpacing, cutout: self.cutout, insets: self.insets, displaySpoilers: self.displaySpoilers, countIfMoreThanOneLineOverMaximum: false))
let _ = apply()
return ImmediateTextNodeLayoutInfo(size: layout.size, truncated: layout.truncated)
}
@ -309,7 +310,7 @@ public class ImmediateTextView: TextView {
self.constrainedSize = constrainedSize
let makeLayout = TextView.asyncLayout(self)
let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, verticalAlignment: self.verticalAlignment, lineSpacing: self.lineSpacing, cutout: self.cutout, insets: self.insets, displaySpoilers: self.displaySpoilers))
let (layout, apply) = makeLayout(TextNodeLayoutArguments(attributedString: self.attributedText, backgroundColor: nil, maximumNumberOfLines: self.maximumNumberOfLines, truncationType: self.truncationType, constrainedSize: constrainedSize, alignment: self.textAlignment, verticalAlignment: self.verticalAlignment, lineSpacing: self.lineSpacing, cutout: self.cutout, insets: self.insets, displaySpoilers: self.displaySpoilers, countIfMoreThanOneLineOverMaximum: false))
let _ = apply()
return layout
}

View File

@ -132,8 +132,9 @@ public final class TextNodeLayoutArguments {
public let textShadowColor: UIColor?
public let textStroke: (UIColor, CGFloat)?
public let displaySpoilers: Bool
public let countIfMoreThanOneLineOverMaximum: Bool
public init(attributedString: NSAttributedString?, backgroundColor: UIColor? = nil, minimumNumberOfLines: Int = 0, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, constrainedSize: CGSize, alignment: NSTextAlignment = .natural, verticalAlignment: TextVerticalAlignment = .top, lineSpacing: CGFloat = 0.12, cutout: TextNodeCutout? = nil, insets: UIEdgeInsets = UIEdgeInsets(), lineColor: UIColor? = nil, textShadowColor: UIColor? = nil, textStroke: (UIColor, CGFloat)? = nil, displaySpoilers: Bool = false) {
public init(attributedString: NSAttributedString?, backgroundColor: UIColor? = nil, minimumNumberOfLines: Int = 0, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, constrainedSize: CGSize, alignment: NSTextAlignment = .natural, verticalAlignment: TextVerticalAlignment = .top, lineSpacing: CGFloat = 0.12, cutout: TextNodeCutout? = nil, insets: UIEdgeInsets = UIEdgeInsets(), lineColor: UIColor? = nil, textShadowColor: UIColor? = nil, textStroke: (UIColor, CGFloat)? = nil, displaySpoilers: Bool = false, countIfMoreThanOneLineOverMaximum: Bool = false) {
self.attributedString = attributedString
self.backgroundColor = backgroundColor
self.minimumNumberOfLines = minimumNumberOfLines
@ -149,6 +150,7 @@ public final class TextNodeLayoutArguments {
self.textShadowColor = textShadowColor
self.textStroke = textStroke
self.displaySpoilers = displaySpoilers
self.countIfMoreThanOneLineOverMaximum = countIfMoreThanOneLineOverMaximum
}
}
@ -177,8 +179,9 @@ public final class TextNodeLayout: NSObject {
public let hasRTL: Bool
public let spoilers: [(NSRange, CGRect)]
public let spoilerWords: [(NSRange, CGRect)]
public let hasMoreThanOneLineOverMaximum: Bool?
fileprivate init(attributedString: NSAttributedString?, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, constrainedSize: CGSize, explicitAlignment: NSTextAlignment, resolvedAlignment: NSTextAlignment, verticalAlignment: TextVerticalAlignment, lineSpacing: CGFloat, cutout: TextNodeCutout?, insets: UIEdgeInsets, size: CGSize, rawTextSize: CGSize, truncated: Bool, firstLineOffset: CGFloat, lines: [TextNodeLine], blockQuotes: [TextNodeBlockQuote], backgroundColor: UIColor?, lineColor: UIColor?, textShadowColor: UIColor?, textStroke: (UIColor, CGFloat)?, displaySpoilers: Bool) {
fileprivate init(attributedString: NSAttributedString?, maximumNumberOfLines: Int, truncationType: CTLineTruncationType, constrainedSize: CGSize, explicitAlignment: NSTextAlignment, resolvedAlignment: NSTextAlignment, verticalAlignment: TextVerticalAlignment, lineSpacing: CGFloat, cutout: TextNodeCutout?, insets: UIEdgeInsets, size: CGSize, rawTextSize: CGSize, truncated: Bool, firstLineOffset: CGFloat, lines: [TextNodeLine], blockQuotes: [TextNodeBlockQuote], backgroundColor: UIColor?, lineColor: UIColor?, textShadowColor: UIColor?, textStroke: (UIColor, CGFloat)?, displaySpoilers: Bool, hasMoreThanOneLineOverMaximum: Bool?) {
self.attributedString = attributedString
self.maximumNumberOfLines = maximumNumberOfLines
self.truncationType = truncationType
@ -222,6 +225,7 @@ public final class TextNodeLayout: NSObject {
self.hasRTL = hasRTL
self.spoilers = spoilers
self.spoilerWords = spoilerWords
self.hasMoreThanOneLineOverMaximum = hasMoreThanOneLineOverMaximum
}
public func areLinesEqual(to other: TextNodeLayout) -> Bool {
@ -931,7 +935,7 @@ public class TextNode: ASDisplayNode {
var maybeTypesetter: CTTypesetter?
maybeTypesetter = CTTypesetterCreateWithAttributedString(attributedString as CFAttributedString)
if maybeTypesetter == nil {
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, explicitAlignment: alignment, resolvedAlignment: resolvedAlignment, verticalAlignment: verticalAlignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), rawTextSize: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], blockQuotes: [], backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor, textStroke: textStroke, displaySpoilers: displaySpoilers)
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, explicitAlignment: alignment, resolvedAlignment: resolvedAlignment, verticalAlignment: verticalAlignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), rawTextSize: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], blockQuotes: [], backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor, textStroke: textStroke, displaySpoilers: displaySpoilers, hasMoreThanOneLineOverMaximum: nil)
}
let typesetter = maybeTypesetter!
@ -948,6 +952,8 @@ public class TextNode: ASDisplayNode {
var bottomCutoutEnabled = false
var bottomCutoutSize = CGSize()
let hasMoreThanOneLineOverMaximum: Bool? = nil
if let topLeft = cutout?.topLeft {
cutoutMinY = -fontLineSpacing
cutoutMaxY = topLeft.height + fontLineSpacing
@ -1262,9 +1268,9 @@ public class TextNode: ASDisplayNode {
}
}
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, explicitAlignment: alignment, resolvedAlignment: resolvedAlignment, verticalAlignment: verticalAlignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(width: ceil(layoutSize.width) + insets.left + insets.right, height: ceil(layoutSize.height) + insets.top + insets.bottom), rawTextSize: CGSize(width: ceil(rawLayoutSize.width) + insets.left + insets.right, height: ceil(rawLayoutSize.height) + insets.top + insets.bottom), truncated: truncated, firstLineOffset: firstLineOffset, lines: lines, blockQuotes: blockQuotes, backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor, textStroke: textStroke, displaySpoilers: displaySpoilers)
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, explicitAlignment: alignment, resolvedAlignment: resolvedAlignment, verticalAlignment: verticalAlignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(width: ceil(layoutSize.width) + insets.left + insets.right, height: ceil(layoutSize.height) + insets.top + insets.bottom), rawTextSize: CGSize(width: ceil(rawLayoutSize.width) + insets.left + insets.right, height: ceil(rawLayoutSize.height) + insets.top + insets.bottom), truncated: truncated, firstLineOffset: firstLineOffset, lines: lines, blockQuotes: blockQuotes, backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor, textStroke: textStroke, displaySpoilers: displaySpoilers, hasMoreThanOneLineOverMaximum: hasMoreThanOneLineOverMaximum)
} else {
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, explicitAlignment: alignment, resolvedAlignment: alignment, verticalAlignment: verticalAlignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), rawTextSize: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], blockQuotes: [], backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor, textStroke: textStroke, displaySpoilers: displaySpoilers)
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, explicitAlignment: alignment, resolvedAlignment: alignment, verticalAlignment: verticalAlignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), rawTextSize: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], blockQuotes: [], backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor, textStroke: textStroke, displaySpoilers: displaySpoilers, hasMoreThanOneLineOverMaximum: nil)
}
}
@ -1588,7 +1594,7 @@ open class TextView: UIView {
var maybeTypesetter: CTTypesetter?
maybeTypesetter = CTTypesetterCreateWithAttributedString(attributedString as CFAttributedString)
if maybeTypesetter == nil {
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, explicitAlignment: alignment, resolvedAlignment: resolvedAlignment, verticalAlignment: verticalAlignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), rawTextSize: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], blockQuotes: [], backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor, textStroke: textStroke, displaySpoilers: displaySpoilers)
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, explicitAlignment: alignment, resolvedAlignment: resolvedAlignment, verticalAlignment: verticalAlignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), rawTextSize: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], blockQuotes: [], backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor, textStroke: textStroke, displaySpoilers: displaySpoilers, hasMoreThanOneLineOverMaximum: nil)
}
let typesetter = maybeTypesetter!
@ -1919,9 +1925,9 @@ open class TextView: UIView {
}
}
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, explicitAlignment: alignment, resolvedAlignment: resolvedAlignment, verticalAlignment: verticalAlignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(width: ceil(layoutSize.width) + insets.left + insets.right, height: ceil(layoutSize.height) + insets.top + insets.bottom), rawTextSize: CGSize(width: ceil(rawLayoutSize.width) + insets.left + insets.right, height: ceil(rawLayoutSize.height) + insets.top + insets.bottom), truncated: truncated, firstLineOffset: firstLineOffset, lines: lines, blockQuotes: blockQuotes, backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor, textStroke: textStroke, displaySpoilers: displaySpoilers)
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, explicitAlignment: alignment, resolvedAlignment: resolvedAlignment, verticalAlignment: verticalAlignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(width: ceil(layoutSize.width) + insets.left + insets.right, height: ceil(layoutSize.height) + insets.top + insets.bottom), rawTextSize: CGSize(width: ceil(rawLayoutSize.width) + insets.left + insets.right, height: ceil(rawLayoutSize.height) + insets.top + insets.bottom), truncated: truncated, firstLineOffset: firstLineOffset, lines: lines, blockQuotes: blockQuotes, backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor, textStroke: textStroke, displaySpoilers: displaySpoilers, hasMoreThanOneLineOverMaximum: nil)
} else {
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, explicitAlignment: alignment, resolvedAlignment: alignment, verticalAlignment: verticalAlignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), rawTextSize: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], blockQuotes: [], backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor, textStroke: textStroke, displaySpoilers: displaySpoilers)
return TextNodeLayout(attributedString: attributedString, maximumNumberOfLines: maximumNumberOfLines, truncationType: truncationType, constrainedSize: constrainedSize, explicitAlignment: alignment, resolvedAlignment: alignment, verticalAlignment: verticalAlignment, lineSpacing: lineSpacingFactor, cutout: cutout, insets: insets, size: CGSize(), rawTextSize: CGSize(), truncated: false, firstLineOffset: 0.0, lines: [], blockQuotes: [], backgroundColor: backgroundColor, lineColor: lineColor, textShadowColor: textShadowColor, textStroke: textStroke, displaySpoilers: displaySpoilers, hasMoreThanOneLineOverMaximum: nil)
}
}

View File

@ -80,7 +80,7 @@ final class LocationPlaceholderNode: ASDisplayNode {
var imageSize = CGSize(width: 144.0, height: 144.0)
var insets = layout.insets(options: [])
if layout.size.width == 460.0 {
if layout.size.width == 320.0 {
insets.top += -60.0
imageSize = CGSize(width: 112.0, height: 112.0)
} else {

View File

@ -119,7 +119,7 @@ final class MediaPickerPlaceholderNode: ASDisplayNode {
var imageSize = CGSize(width: 144.0, height: 144.0)
var insets = layout.insets(options: [])
if layout.size.width == 460.0 {
if layout.size.width == 320.0 {
insets.top += -60.0
imageSize = CGSize(width: 112.0, height: 112.0)
} else {

View File

@ -0,0 +1,32 @@
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
swift_library(
name = "PremiumUI",
module_name = "PremiumUI",
srcs = glob([
"Sources/**/*.swift",
]),
copts = [
"-warnings-as-errors",
],
deps = [
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
"//submodules/Display:Display",
"//submodules/Postbox:Postbox",
"//submodules/TelegramCore:TelegramCore",
"//submodules/TelegramPresentationData:TelegramPresentationData",
"//submodules/TelegramUIPreferences:TelegramUIPreferences",
"//submodules/AccountContext:AccountContext",
"//submodules/TelegramStringFormatting:TelegramStringFormatting",
"//submodules/AppBundle:AppBundle",
"//submodules/AnimatedStickerNode:AnimatedStickerNode",
"//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode",
"//submodules/SolidRoundedButtonNode:SolidRoundedButtonNode",
"//submodules/PresentationDataUtils:PresentationDataUtils",
"//submodules/ReactionSelectionNode:ReactionSelectionNode",
],
visibility = [
"//visibility:public",
],
)

View File

@ -0,0 +1,240 @@
import Foundation
import UIKit
import Display
import AsyncDisplayKit
import Postbox
import TelegramCore
import SwiftSignalKit
import AccountContext
import TelegramPresentationData
import PresentationDataUtils
import SolidRoundedButtonNode
import AppBundle
public final class PremiumStickersScreen: ViewController {
private let context: AccountContext
private var presentationData: PresentationData
private var presentationDataDisposable: Disposable?
private let updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?
private let reactions: [AvailableReactions.Reaction]
public var proceed: (() -> Void)?
private class Node: ViewControllerTracingNode, UIGestureRecognizerDelegate {
private weak var controller: PremiumStickersScreen?
private var presentationData: PresentationData
private let blurView: UIVisualEffectView
private let vibrancyView: UIVisualEffectView
private let dimNode: ASDisplayNode
private let darkDimNode: ASDisplayNode
private let containerNode: ASDisplayNode
private let textNode: ImmediateTextNode
private let proceedButton: SolidRoundedButtonNode
private let cancelButton: HighlightableButtonNode
private let carouselNode: ReactionCarouselNode
private var validLayout: ContainerViewLayout?
init(controller: PremiumStickersScreen) {
self.controller = controller
self.presentationData = controller.presentationData
self.dimNode = ASDisplayNode()
let blurEffect = UIBlurEffect(style: self.presentationData.theme.overallDarkAppearance ? .dark : .light)
self.blurView = UIVisualEffectView(effect: blurEffect)
self.blurView.isUserInteractionEnabled = false
self.vibrancyView = UIVisualEffectView(effect: UIVibrancyEffect(blurEffect: blurEffect))
self.darkDimNode = ASDisplayNode()
self.darkDimNode.alpha = 0.0
self.darkDimNode.backgroundColor = self.presentationData.theme.contextMenu.dimColor
self.darkDimNode.isUserInteractionEnabled = false
self.dimNode.backgroundColor = UIColor(white: self.presentationData.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.5)
self.containerNode = ASDisplayNode()
self.textNode = ImmediateTextNode()
self.textNode.displaysAsynchronously = false
self.textNode.textAlignment = .center
self.textNode.maximumNumberOfLines = 0
self.textNode.lineSpacing = 0.1
self.textNode.attributedText = NSAttributedString(string: self.presentationData.strings.Premium_Reactions_Description, font: Font.regular(17.0), textColor: self.presentationData.theme.contextMenu.secondaryColor)
self.textNode.alpha = 1.0
self.proceedButton = SolidRoundedButtonNode(title: self.presentationData.strings.Premium_Reactions_Proceed, icon: UIImage(bundleImageName: "Premium/ButtonIcon"), theme: SolidRoundedButtonTheme(theme: self.presentationData.theme), height: 50.0, cornerRadius: 11.0, gloss: true)
self.cancelButton = HighlightableButtonNode()
self.cancelButton.setTitle(self.presentationData.strings.Common_Cancel, with: Font.regular(17.0), with: self.presentationData.theme.list.itemAccentColor, for: .normal)
self.carouselNode = ReactionCarouselNode(context: controller.context, theme: controller.presentationData.theme, reactions: controller.reactions)
super.init()
self.addSubnode(self.dimNode)
self.addSubnode(self.darkDimNode)
self.addSubnode(self.containerNode)
self.containerNode.addSubnode(self.proceedButton)
self.containerNode.addSubnode(self.cancelButton)
self.addSubnode(self.carouselNode)
self.proceedButton.pressed = { [weak self] in
self?.animateOut()
}
self.cancelButton.addTarget(self, action: #selector(self.cancelPressed), forControlEvents: .touchUpInside)
}
override func didLoad() {
super.didLoad()
self.view.insertSubview(self.blurView, aboveSubview: self.dimNode.view)
self.blurView.contentView.addSubview(self.vibrancyView)
self.vibrancyView.contentView.addSubview(self.textNode.view)
self.dimNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimNodeTap(_:))))
}
func updatePresentationData(_ presentationData: PresentationData) {
self.presentationData = presentationData
self.dimNode.backgroundColor = UIColor(white: self.presentationData.theme.overallDarkAppearance ? 0.0 : 1.0, alpha: 0.5)
self.darkDimNode.backgroundColor = self.presentationData.theme.contextMenu.dimColor
self.blurView.effect = UIBlurEffect(style: self.presentationData.theme.overallDarkAppearance ? .dark : .light)
}
func animateIn() {
self.dimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
self.blurView.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
self.carouselNode.layer.animatePosition(from: CGPoint(x: 312.0, y: 252.0), to: self.carouselNode.position, duration: 0.45, timingFunction: kCAMediaTimingFunctionSpring)
self.carouselNode.layer.animateScale(from: 0.001, to: 1.0, duration: 0.45, timingFunction: kCAMediaTimingFunctionSpring)
self.carouselNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
self.carouselNode.animateIn()
self.containerNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
self.containerNode.layer.animateScale(from: 0.95, to: 1.0, duration: 0.3)
self.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
self.vibrancyView.layer.animateScale(from: 0.95, to: 1.0, duration: 0.3)
}
func animateOut() {
self.containerNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
self.textNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
self.carouselNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
self.blurView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak self] _ in
if let strongSelf = self {
strongSelf.controller?.dismiss(animated: false, completion: nil)
}
})
}
func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
self.validLayout = layout
transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(), size: layout.size))
transition.updateFrame(node: self.darkDimNode, frame: CGRect(origin: CGPoint(), size: layout.size))
transition.updateFrame(view: self.blurView, frame: CGRect(origin: CGPoint(), size: layout.size))
transition.updateFrame(view: self.vibrancyView, frame: CGRect(origin: CGPoint(), size: layout.size))
transition.updateFrame(node: self.containerNode, frame: CGRect(origin: CGPoint(), size: layout.size))
let carouselFrame = CGRect(origin: CGPoint(x: 0.0, y: 100.0), size: CGSize(width: layout.size.width, height: layout.size.width))
self.carouselNode.updateLayout(size: carouselFrame.size, transition: transition)
transition.updateFrame(node: self.carouselNode, frame: carouselFrame)
let sideInset: CGFloat = 16.0
let cancelSize = self.cancelButton.measure(layout.size)
self.cancelButton.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - cancelSize.width) / 2.0), y: layout.size.height - cancelSize.height - 49.0), size: cancelSize)
let buttonWidth = layout.size.width - sideInset * 2.0
let buttonHeight = self.proceedButton.updateLayout(width: buttonWidth, transition: transition)
self.proceedButton.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - buttonWidth) / 2.0), y: layout.size.height - cancelSize.height - 49.0 - buttonHeight - 23.0), size: CGSize(width: buttonWidth, height: buttonHeight))
let textSize = self.textNode.updateLayout(CGSize(width: layout.size.width - sideInset * 5.0, height: CGFloat.greatestFiniteMagnitude))
self.textNode.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - textSize.width) / 2.0), y: layout.size.height - cancelSize.height - 48.0 - buttonHeight - 20.0 - textSize.height - 31.0), size: textSize)
}
@objc private func dimNodeTap(_ recognizer: UITapGestureRecognizer) {
if case .ended = recognizer.state {
self.cancelPressed()
}
}
@objc private func cancelPressed() {
self.animateOut()
}
}
private var controllerNode: Node {
return self.displayNode as! Node
}
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, reactions: [AvailableReactions.Reaction]) {
self.context = context
self.reactions = reactions
let presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
self.presentationData = presentationData
self.updatedPresentationData = updatedPresentationData
super.init(navigationBarPresentationData: nil)
self.statusBar.statusBarStyle = .Ignore
self.supportedOrientations = ViewControllerSupportedOrientations(regularSize: .all, compactSize: .portrait)
self.presentationDataDisposable = ((updatedPresentationData?.signal ?? context.sharedContext.presentationData)
|> deliverOnMainQueue).start(next: { [weak self] presentationData in
if let strongSelf = self {
let previousTheme = strongSelf.presentationData.theme
let previousStrings = strongSelf.presentationData.strings
strongSelf.presentationData = presentationData
if previousTheme !== presentationData.theme || previousStrings !== presentationData.strings {
strongSelf.controllerNode.updatePresentationData(strongSelf.presentationData)
}
}
})
}
required init(coder aDecoder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
deinit {
self.presentationDataDisposable?.dispose()
}
override public func loadDisplayNode() {
self.displayNode = Node(controller: self)
super.displayNodeDidLoad()
}
private var didAppear = false
public override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
if !self.didAppear {
self.didAppear = true
self.controllerNode.animateIn()
}
}
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
super.containerLayoutUpdated(layout, transition: transition)
self.controllerNode.containerLayoutUpdated(layout, transition: transition)
}
}

View File

@ -0,0 +1,307 @@
import Foundation
import UIKit
import Display
import AsyncDisplayKit
import TelegramCore
import AccountContext
import ReactionSelectionNode
import TelegramPresentationData
import AccountContext
private let itemSize = CGSize(width: 110.0, height: 110.0)
final class ReactionCarouselNode: ASDisplayNode, UIScrollViewDelegate {
private let context: AccountContext
private let theme: PresentationTheme
private let reactions: [AvailableReactions.Reaction]
private var itemNodes: [ReactionNode] = []
private let scrollNode: ASScrollNode
private let tapNode: ASDisplayNode
private var standaloneReactionAnimation: StandaloneReactionAnimation?
private var animator: DisplayLinkAnimator?
private var currentPosition: CGFloat = 0.0
private var validLayout: CGSize?
init(context: AccountContext, theme: PresentationTheme, reactions: [AvailableReactions.Reaction]) {
self.context = context
self.theme = theme
self.reactions = Array(reactions.shuffled().prefix(6))
self.scrollNode = ASScrollNode()
self.tapNode = ASDisplayNode()
super.init()
self.addSubnode(self.scrollNode)
self.scrollNode.addSubnode(self.tapNode)
self.setup()
}
override func didLoad() {
super.didLoad()
self.scrollNode.view.delegate = self
self.scrollNode.view.showsHorizontalScrollIndicator = false
self.scrollNode.view.canCancelContentTouches = true
self.tapNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.reactionTapped(_:))))
}
@objc private func reactionTapped(_ gestureRecognizer: UITapGestureRecognizer) {
guard self.animator == nil, self.standaloneReactionAnimation == nil, self.scrollStartPosition == nil else {
return
}
let point = gestureRecognizer.location(in: self.view)
guard let index = self.itemNodes.firstIndex(where: { $0.frame.contains(point) }) else {
return
}
self.scrollTo(index, playReaction: true, duration: 0.4)
}
func animateIn() {
self.scrollTo(1, playReaction: true, duration: 0.5, clockwise: true)
}
func scrollTo(_ index: Int, playReaction: Bool, duration: Double, clockwise: Bool? = nil) {
guard index >= 0 && index < self.itemNodes.count else {
return
}
let delta = 1.0 / CGFloat(self.itemNodes.count)
let startPosition = self.currentPosition
let newPosition = delta * CGFloat(index)
var change = newPosition - startPosition
if let clockwise = clockwise {
if clockwise {
if change > 0.0 {
change = change - 1.0
}
} else {
if change < 0.0 {
change = 1.0 + change
}
}
} else {
if change > 0.5 {
change = change - 1.0
} else if change < -0.5 {
change = 1.0 + change
}
}
self.animator = DisplayLinkAnimator(duration: duration * UIView.animationDurationFactor(), from: 0.0, to: 1.0, update: { [weak self] t in
let t = listViewAnimationCurveSystem(t)
var updatedPosition = startPosition + change * t
while updatedPosition >= 1.0 {
updatedPosition -= 1.0
}
while updatedPosition < 0.0 {
updatedPosition += 1.0
}
self?.currentPosition = updatedPosition
if let size = self?.validLayout {
self?.updateLayout(size: size, transition: .immediate)
}
}, completion: { [weak self] in
self?.animator = nil
if playReaction {
self?.playReaction()
}
})
}
func setup() {
for reaction in self.reactions {
guard let centerAnimation = reaction.centerAnimation else {
continue
}
guard let aroundAnimation = reaction.aroundAnimation else {
continue
}
let itemNode = ReactionNode(context: self.context, theme: self.theme, item: ReactionItem(
reaction: ReactionItem.Reaction(rawValue: reaction.value),
appearAnimation: reaction.appearAnimation,
stillAnimation: reaction.selectAnimation,
listAnimation: centerAnimation,
largeListAnimation: reaction.activateAnimation,
applicationAnimation: aroundAnimation,
largeApplicationAnimation: reaction.effectAnimation
), hasAppearAnimation: false)
itemNode.isUserInteractionEnabled = false
self.addSubnode(itemNode)
self.itemNodes.append(itemNode)
}
}
private var ignoreContentOffsetChange = false
private func resetScrollPosition() {
self.scrollStartPosition = nil
self.ignoreContentOffsetChange = true
self.scrollNode.view.contentOffset = CGPoint(x: 5000.0 - self.scrollNode.frame.width * 0.5, y: 0.0)
self.ignoreContentOffsetChange = false
}
func playReaction() {
guard self.standaloneReactionAnimation == nil else {
return
}
let delta = 1.0 / CGFloat(self.itemNodes.count)
let index = max(0, min(self.itemNodes.count - 1, Int(round(self.currentPosition / delta))))
let reaction = self.reactions[index]
let targetView = self.itemNodes[index].view
let standaloneReactionAnimation = StandaloneReactionAnimation()
self.standaloneReactionAnimation = standaloneReactionAnimation
guard let supernode = self.supernode else {
return
}
guard let centerAnimation = reaction.centerAnimation else {
return
}
guard let aroundAnimation = reaction.aroundAnimation else {
return
}
self.scrollNode.view.isScrollEnabled = false
supernode.addSubnode(standaloneReactionAnimation)
standaloneReactionAnimation.frame = supernode.bounds
standaloneReactionAnimation.animateReactionSelection(
context: self.context, theme: self.theme, reaction: ReactionItem(
reaction: ReactionItem.Reaction(rawValue: reaction.value),
appearAnimation: reaction.appearAnimation,
stillAnimation: reaction.selectAnimation,
listAnimation: centerAnimation,
largeListAnimation: reaction.activateAnimation,
applicationAnimation: aroundAnimation,
largeApplicationAnimation: reaction.effectAnimation
),
avatarPeers: [],
playHaptic: false,
isLarge: true,
targetView: targetView,
addStandaloneReactionAnimation: nil,
completion: { [weak standaloneReactionAnimation, weak self] in
standaloneReactionAnimation?.removeFromSupernode()
self?.standaloneReactionAnimation = nil
self?.scrollNode.view.isScrollEnabled = true
}
)
}
private var scrollStartPosition: (contentOffset: CGFloat, position: CGFloat)?
func scrollViewWillBeginDragging(_ scrollView: UIScrollView) {
if self.scrollStartPosition == nil {
self.scrollStartPosition = (scrollView.contentOffset.x, self.currentPosition)
}
}
func scrollViewDidScroll(_ scrollView: UIScrollView) {
guard !self.ignoreContentOffsetChange, let (startContentOffset, startPosition) = self.scrollStartPosition else {
return
}
let delta = scrollView.contentOffset.x - startContentOffset
let positionDelta = delta * -0.001
var updatedPosition = startPosition + positionDelta
while updatedPosition >= 1.0 {
updatedPosition -= 1.0
}
while updatedPosition < 0.0 {
updatedPosition += 1.0
}
self.currentPosition = updatedPosition
if let size = self.validLayout {
self.updateLayout(size: size, transition: .immediate)
}
}
func scrollViewWillEndDragging(_ scrollView: UIScrollView, withVelocity velocity: CGPoint, targetContentOffset: UnsafeMutablePointer<CGPoint>) {
guard let (startContentOffset, _) = self.scrollStartPosition, abs(velocity.x) > 0.0 else {
return
}
let delta = 1.0 / CGFloat(self.itemNodes.count)
let scrollDelta = targetContentOffset.pointee.x - startContentOffset
let positionDelta = scrollDelta * -0.001
let positionCounts = round(positionDelta / delta)
let adjustedPositionDelta = delta * positionCounts
let adjustedScrollDelta = adjustedPositionDelta * -1000.0
targetContentOffset.pointee = CGPoint(x: startContentOffset + adjustedScrollDelta, y: 0.0)
}
func scrollViewDidEndDragging(_ scrollView: UIScrollView, willDecelerate decelerate: Bool) {
if !decelerate {
self.resetScrollPosition()
let delta = 1.0 / CGFloat(self.itemNodes.count)
let index = max(0, min(self.itemNodes.count - 1, Int(round(self.currentPosition / delta))))
self.scrollTo(index, playReaction: true, duration: 0.2)
}
}
func scrollViewDidEndDecelerating(_ scrollView: UIScrollView) {
self.resetScrollPosition()
self.playReaction()
}
func updateLayout(size: CGSize, transition: ContainedViewLayoutTransition) {
self.validLayout = size
self.scrollNode.frame = CGRect(origin: CGPoint(), size: size)
if self.scrollNode.view.contentSize.width.isZero {
self.scrollNode.view.contentSize = CGSize(width: 10000000, height: size.height)
self.tapNode.frame = CGRect(origin: CGPoint(), size: self.scrollNode.view.contentSize)
self.resetScrollPosition()
}
let delta = 1.0 / CGFloat(self.itemNodes.count)
let areaSize = CGSize(width: floor(size.width * 0.7), height: size.height * 0.5)
var i = 0
for itemNode in self.itemNodes {
var angle = CGFloat.pi * 0.5 + CGFloat(i) * delta * CGFloat.pi * 2.0 - self.currentPosition * CGFloat.pi * 2.0
if angle < 0.0 {
angle = CGFloat.pi * 2.0 + angle
}
if angle > CGFloat.pi * 2.0 {
angle = angle - CGFloat.pi * 2.0
}
func calculateRelativeAngle(_ angle: CGFloat) -> CGFloat {
var relativeAngle = angle - CGFloat.pi * 0.5
if relativeAngle > CGFloat.pi {
relativeAngle = (2.0 * CGFloat.pi - relativeAngle) * -1.0
}
return relativeAngle
}
let relativeAngle = calculateRelativeAngle(angle)
let distance = abs(relativeAngle) / CGFloat.pi
let point = CGPoint(
x: cos(angle),
y: sin(angle)
)
let itemFrame = CGRect(origin: CGPoint(x: size.width * 0.5 + point.x * areaSize.width * 0.5 - itemSize.width * 0.5, y: size.height * 0.5 + point.y * areaSize.height * 0.5 - itemSize.height * 0.5), size: itemSize)
itemNode.bounds = CGRect(origin: CGPoint(), size: itemFrame.size)
itemNode.position = itemFrame.center
itemNode.updateLayout(size: itemFrame.size, isExpanded: false, largeExpanded: false, isPreviewing: false, transition: transition)
transition.updateTransformScale(node: itemNode, scale: 1.0 - distance * 0.45)
i += 1
}
}
}

View File

@ -936,7 +936,7 @@ public final class StandaloneReactionAnimation: ASDisplayNode {
self.animateReactionSelection(context: context, theme: theme, reaction: reaction, avatarPeers: avatarPeers, playHaptic: playHaptic, isLarge: isLarge, targetView: targetView, addStandaloneReactionAnimation: addStandaloneReactionAnimation, currentItemNode: nil, completion: completion)
}
func animateReactionSelection(context: AccountContext, theme: PresentationTheme, reaction: ReactionItem, avatarPeers: [EnginePeer], playHaptic: Bool, isLarge: Bool, targetView: UIView, addStandaloneReactionAnimation: ((StandaloneReactionAnimation) -> Void)?, currentItemNode: ReactionNode?, completion: @escaping () -> Void) {
func animateReactionSelection(context: AccountContext, theme: PresentationTheme, reaction: ReactionItem, avatarPeers: [EnginePeer], playHaptic: Bool, isLarge: Bool, targetView: UIView, addStandaloneReactionAnimation: ((StandaloneReactionAnimation) -> Void)?, currentItemNode: ReactionNode?, completion: @escaping () -> Void) {
guard let sourceSnapshotView = targetView.snapshotContentTree() else {
completion()
return

View File

@ -42,7 +42,7 @@ protocol ReactionItemNode: ASDisplayNode {
func updateLayout(size: CGSize, isExpanded: Bool, largeExpanded: Bool, isPreviewing: Bool, transition: ContainedViewLayoutTransition)
}
final class ReactionNode: ASDisplayNode, ReactionItemNode {
public final class ReactionNode: ASDisplayNode, ReactionItemNode {
let context: AccountContext
let item: ReactionItem
@ -64,14 +64,16 @@ final class ReactionNode: ASDisplayNode, ReactionItemNode {
var expandedAnimationDidBegin: (() -> Void)?
init(context: AccountContext, theme: PresentationTheme, item: ReactionItem) {
public init(context: AccountContext, theme: PresentationTheme, item: ReactionItem, hasAppearAnimation: Bool = true) {
self.context = context
self.item = item
self.staticAnimationNode = AnimatedStickerNode()
self.staticAnimationNode.isHidden = true
self.animateInAnimationNode = AnimatedStickerNode()
if hasAppearAnimation {
self.staticAnimationNode.isHidden = true
self.animateInAnimationNode = AnimatedStickerNode()
}
super.init()
@ -111,7 +113,7 @@ final class ReactionNode: ASDisplayNode, ReactionItemNode {
}
}
func updateLayout(size: CGSize, isExpanded: Bool, largeExpanded: Bool, isPreviewing: Bool, transition: ContainedViewLayoutTransition) {
public func updateLayout(size: CGSize, isExpanded: Bool, largeExpanded: Bool, isPreviewing: Bool, transition: ContainedViewLayoutTransition) {
let intrinsicSize = size
let animationSize = self.item.stillAnimation.dimensions?.cgSize ?? CGSize(width: 512.0, height: 512.0)
@ -300,11 +302,32 @@ final class ReactionNode: ASDisplayNode, ReactionItemNode {
final class PremiumReactionsNode: ASDisplayNode, ReactionItemNode {
var isExtracted: Bool = false
let imageNode: ASImageNode
override init() {
self.imageNode = ASImageNode()
self.imageNode.contentMode = .center
self.imageNode.displaysAsynchronously = false
self.imageNode.image = UIImage(bundleImageName: "Premium/ReactionIcon")
self.imageNode.isUserInteractionEnabled = false
self.imageNode.alpha = 0.5
super.init()
self.addSubnode(self.imageNode)
}
override func didLoad() {
super.didLoad()
self.imageNode.layer.compositingFilter = "softLightBlendMode"
}
func appear(animated: Bool) {
}
func updateLayout(size: CGSize, isExpanded: Bool, largeExpanded: Bool, isPreviewing: Bool, transition: ContainedViewLayoutTransition) {
self.imageNode.frame = CGRect(origin: CGPoint(), size: size)
}
}

View File

@ -51,6 +51,7 @@ public final class SolidRoundedButtonNode: ASDisplayNode {
private var theme: SolidRoundedButtonTheme
private var font: SolidRoundedButtonFont
private var fontSize: CGFloat
private let gloss: Bool
private let buttonBackgroundNode: ASDisplayNode
@ -100,9 +101,7 @@ public final class SolidRoundedButtonNode: ASDisplayNode {
}
}
}
private let gloss: Bool
public init(title: String? = nil, icon: UIImage? = nil, theme: SolidRoundedButtonTheme, font: SolidRoundedButtonFont = .bold, fontSize: CGFloat = 17.0, height: CGFloat = 48.0, cornerRadius: CGFloat = 24.0, gloss: Bool = false) {
self.theme = theme
self.font = font
@ -167,50 +166,6 @@ public final class SolidRoundedButtonNode: ASDisplayNode {
}
}
public func transitionToProgress() {
guard self.progressNode == nil else {
return
}
self.isUserInteractionEnabled = false
let buttonOffset = self.buttonBackgroundNode.frame.minX
let buttonWidth = self.buttonBackgroundNode.frame.width
let progressFrame = CGRect(origin: CGPoint(x: floorToScreenPixels(buttonOffset + (buttonWidth - self.buttonHeight) / 2.0), y: 0.0), size: CGSize(width: self.buttonHeight, height: self.buttonHeight))
let progressNode = ASImageNode()
progressNode.displaysAsynchronously = false
progressNode.frame = progressFrame
progressNode.image = generateIndefiniteActivityIndicatorImage(color: self.buttonBackgroundNode.backgroundColor ?? .clear, diameter: self.buttonHeight, lineWidth: 2.0 + UIScreenPixel)
self.insertSubnode(progressNode, at: 0)
self.progressNode = progressNode
let basicAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
basicAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
basicAnimation.duration = 0.5
basicAnimation.fromValue = NSNumber(value: Float(0.0))
basicAnimation.toValue = NSNumber(value: Float.pi * 2.0)
basicAnimation.repeatCount = Float.infinity
basicAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
basicAnimation.beginTime = 1.0
progressNode.layer.add(basicAnimation, forKey: "progressRotation")
self.buttonBackgroundNode.cornerRadius = self.buttonHeight / 2.0
self.buttonBackgroundNode.layer.animate(from: self.buttonCornerRadius as NSNumber, to: self.buttonHeight / 2.0 as NSNumber, keyPath: "cornerRadius", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: 0.2)
self.buttonBackgroundNode.layer.animateFrame(from: self.buttonBackgroundNode.frame, to: progressFrame, duration: 0.2)
self.buttonBackgroundNode.alpha = 0.0
self.buttonBackgroundNode.layer.animateAlpha(from: 0.55, to: 0.0, duration: 0.2, removeOnCompletion: false)
progressNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, removeOnCompletion: false)
self.titleNode.alpha = 0.0
self.titleNode.layer.animateAlpha(from: 0.55, to: 0.0, duration: 0.2)
self.subtitleNode.alpha = 0.0
self.subtitleNode.layer.animateAlpha(from: 0.55, to: 0.0, duration: 0.2)
}
public override func didLoad() {
super.didLoad()
@ -222,6 +177,11 @@ public final class SolidRoundedButtonNode: ASDisplayNode {
let shimmerView = ShimmerEffectForegroundView()
self.shimmerView = shimmerView
if #available(iOS 13.0, *) {
shimmerView.layer.cornerCurve = .continuous
shimmerView.layer.cornerRadius = self.buttonCornerRadius
}
let borderView = UIView()
borderView.isUserInteractionEnabled = false
self.borderView = borderView
@ -362,6 +322,51 @@ public final class SolidRoundedButtonNode: ASDisplayNode {
@objc private func buttonPressed() {
self.pressed?()
}
public func transitionToProgress() {
guard self.progressNode == nil else {
return
}
self.isUserInteractionEnabled = false
let buttonOffset = self.buttonBackgroundNode.frame.minX
let buttonWidth = self.buttonBackgroundNode.frame.width
let progressFrame = CGRect(origin: CGPoint(x: floorToScreenPixels(buttonOffset + (buttonWidth - self.buttonHeight) / 2.0), y: 0.0), size: CGSize(width: self.buttonHeight, height: self.buttonHeight))
let progressNode = ASImageNode()
progressNode.displaysAsynchronously = false
progressNode.frame = progressFrame
progressNode.image = generateIndefiniteActivityIndicatorImage(color: self.buttonBackgroundNode.backgroundColor ?? .clear, diameter: self.buttonHeight, lineWidth: 2.0 + UIScreenPixel)
self.insertSubnode(progressNode, at: 0)
self.progressNode = progressNode
let basicAnimation = CABasicAnimation(keyPath: "transform.rotation.z")
basicAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut)
basicAnimation.duration = 0.5
basicAnimation.fromValue = NSNumber(value: Float(0.0))
basicAnimation.toValue = NSNumber(value: Float.pi * 2.0)
basicAnimation.repeatCount = Float.infinity
basicAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear)
basicAnimation.beginTime = 1.0
progressNode.layer.add(basicAnimation, forKey: "progressRotation")
self.buttonBackgroundNode.cornerRadius = self.buttonHeight / 2.0
self.buttonBackgroundNode.layer.animate(from: self.buttonCornerRadius as NSNumber, to: self.buttonHeight / 2.0 as NSNumber, keyPath: "cornerRadius", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: 0.2)
self.buttonBackgroundNode.layer.animateFrame(from: self.buttonBackgroundNode.frame, to: progressFrame, duration: 0.2)
self.buttonBackgroundNode.alpha = 0.0
self.buttonBackgroundNode.layer.animateAlpha(from: 0.55, to: 0.0, duration: 0.2, removeOnCompletion: false)
progressNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, removeOnCompletion: false)
self.titleNode.alpha = 0.0
self.titleNode.layer.animateAlpha(from: 0.55, to: 0.0, duration: 0.2)
self.subtitleNode.alpha = 0.0
self.subtitleNode.layer.animateAlpha(from: 0.55, to: 0.0, duration: 0.2)
}
}
public final class SolidRoundedButtonView: UIView {

View File

@ -238,7 +238,7 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol
}
})))
}
return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, item: .pack(item), isLocked: item.file.isPremiumSticker && !hasPremium, menu: menuItems))
return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, item: .pack(item), isLocked: item.file.isPremiumSticker && !hasPremium, menu: menuItems))
} else {
return nil
}

View File

@ -31,15 +31,7 @@ final class StickerPackPreviewGridItem: GridItem {
let isEmpty: Bool
let section: GridSection? = nil
var customItemSize: CGSize? {
if self.isPremium {
return CGSize(width: 129.0, height: 129.0)
} else {
return nil
}
}
init(account: Account, stickerItem: StickerPackItem?, interaction: StickerPackPreviewInteraction, theme: PresentationTheme, isPremium: Bool, isLocked: Bool, isEmpty: Bool) {
self.account = account
self.stickerItem = stickerItem
@ -75,9 +67,7 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
private var animationNode: AnimatedStickerNode?
private var placeholderNode: StickerShimmerEffectNode
private var lockBackground: UIVisualEffectView?
private var lockTintView: UIView?
private var lockIconNode: ASImageNode?
private var lockBackground: UIImageView?
private var theme: PresentationTheme?
@ -177,43 +167,21 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
self.isLocked = isLocked
if isLocked {
let lockBackground: UIVisualEffectView
let lockIconNode: ASImageNode
if let currentBackground = self.lockBackground, let currentIcon = self.lockIconNode {
let lockBackground: UIImageView
if let currentBackground = self.lockBackground {
lockBackground = currentBackground
lockIconNode = currentIcon
} else {
let effect: UIBlurEffect
if #available(iOS 10.0, *) {
effect = UIBlurEffect(style: .regular)
} else {
effect = UIBlurEffect(style: .light)
}
lockBackground = UIVisualEffectView(effect: effect)
lockBackground = UIImageView()
lockBackground.clipsToBounds = true
lockBackground.isUserInteractionEnabled = false
lockIconNode = ASImageNode()
lockIconNode.displaysAsynchronously = false
lockIconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Stickers/Lock"), color: .white)
let lockTintView = UIView()
lockTintView.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.15)
lockBackground.contentView.addSubview(lockTintView)
lockBackground.image = UIImage(bundleImageName: "Premium/StickerIcon")
self.lockBackground = lockBackground
self.lockTintView = lockTintView
self.lockIconNode = lockIconNode
self.view.addSubview(lockBackground)
self.addSubnode(lockIconNode)
}
} else if let lockBackground = self.lockBackground, let lockTintView = self.lockTintView, let lockIconNode = self.lockIconNode {
} else if let lockBackground = self.lockBackground {
self.lockBackground = nil
self.lockTintView = nil
self.lockIconNode = nil
lockBackground.removeFromSuperview()
lockTintView.removeFromSuperview()
lockIconNode.removeFromSupernode()
}
if let stickerItem = stickerItem {
@ -279,6 +247,9 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
self.placeholderNode.alpha = 1.0
}
}
self.animationNode?.alpha = isLocked ? 0.5 : 1.0
self.currentState = (account, stickerItem)
self.setNeedsLayout()
}
@ -314,18 +285,10 @@ final class StickerPackPreviewGridItemNode: GridItemNode {
self.placeholderNode.update(backgroundColor: theme.list.itemBlocksBackgroundColor, foregroundColor: theme.list.mediaPlaceholderColor, shimmeringColor: theme.list.itemBlocksBackgroundColor.withAlphaComponent(0.4), data: item.file.immediateThumbnailData, size: placeholderFrame.size)
}
if let lockBackground = self.lockBackground, let lockTintView = self.lockTintView, let lockIconNode = self.lockIconNode {
let lockSize = CGSize(width: 30.0, height: 30.0)
if let lockBackground = self.lockBackground {
let lockSize = CGSize(width: 32.0, height: 32.0)
let lockBackgroundFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((bounds.width - lockSize.width) / 2.0), y: bounds.height - lockSize.height - 6.0), size: lockSize)
lockBackground.frame = lockBackgroundFrame
lockBackground.layer.cornerRadius = lockSize.width / 2.0
if #available(iOS 13.0, *) {
lockBackground.layer.cornerCurve = .circular
}
lockTintView.frame = CGRect(origin: CGPoint(), size: lockBackgroundFrame.size)
if let icon = lockIconNode.image {
lockIconNode.frame = CGRect(origin: CGPoint(x: lockBackgroundFrame.minX + floorToScreenPixels((lockBackgroundFrame.width - icon.size.width) / 2.0), y: lockBackgroundFrame.minY + floorToScreenPixels((lockBackgroundFrame.height - icon.size.height) / 2.0)), size: icon.size)
}
}
}

View File

@ -209,6 +209,13 @@ private final class StickerPackContainer: ASDisplayNode {
}
}
// self.gridNode.visibleContentOffsetChanged = { [weak self] offset in
// guard let strongSelf = self else {
// return
// }
//
// }
self.gridNode.interactiveScrollingWillBeEnded = { [weak self] contentOffset, velocity, targetOffset -> CGPoint in
guard let strongSelf = self, !strongSelf.isDismissed else {
return targetOffset
@ -360,7 +367,7 @@ private final class StickerPackContainer: ASDisplayNode {
}
})))
}
return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, item: .pack(item), isLocked: item.file.isPremiumSticker && !hasPremium, menu: menuItems))
return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, item: .pack(item), isLocked: item.file.isPremiumSticker && !hasPremium, menu: menuItems))
} else {
return nil
}
@ -628,22 +635,6 @@ private final class StickerPackContainer: ASDisplayNode {
}
if !premiumItems.isEmpty {
var stableId: Int?
inner: for entry in self.currentEntries {
if case let .premiumHeader(_, currentStableId, _) = entry {
stableId = currentStableId
break inner
}
}
let resolvedStableId: Int
if let stableId = stableId {
resolvedStableId = stableId
} else {
resolvedStableId = self.nextStableId
self.nextStableId += 1
}
entries.append(.premiumHeader(index: entries.count, stableId: resolvedStableId, count: Int32(premiumItems.count)))
for item in premiumItems {
addItem(item, true, !hasPremium)
}
@ -827,9 +818,11 @@ private final class StickerPackContainer: ASDisplayNode {
transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame)
transition.updateFrame(node: self.titleContainer, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX + floor((backgroundFrame.width) / 2.0), y: backgroundFrame.minY + floor((56.0) / 2.0)), size: CGSize()))
transition.updateFrame(node: self.titleSeparatorNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX, y: backgroundFrame.minY + 56.0 - UIScreenPixel), size: CGSize(width: backgroundFrame.width, height: UIScreenPixel)))
self.titleSeparatorNode.alpha = unclippedBackgroundY < minBackgroundY ? 1.0 : 0.0
transition.updateFrame(node: self.topContainerNode, frame: CGRect(origin: CGPoint(x: backgroundFrame.minX, y: backgroundFrame.minY), size: CGSize(width: backgroundFrame.width, height: 56.0)))
let transition = ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut)
transition.updateAlpha(node: self.titleSeparatorNode, alpha: unclippedBackgroundY < minBackgroundY ? 1.0 : 0.0)
}
private func enqueueTransaction(_ transaction: StickerPackPreviewGridTransaction) {

View File

@ -12,6 +12,7 @@ import ContextUI
import SolidRoundedButtonNode
import TelegramPresentationData
import AccountContext
import AppBundle
public enum StickerPreviewPeekItem: Equatable {
case pack(StickerPackItem)
@ -30,13 +31,15 @@ public enum StickerPreviewPeekItem: Equatable {
public final class StickerPreviewPeekContent: PeekControllerContent {
let account: Account
let theme: PresentationTheme
let strings: PresentationStrings
public let item: StickerPreviewPeekItem
let isLocked: Bool
let menu: [ContextMenuItem]
public init(account: Account, theme: PresentationTheme, item: StickerPreviewPeekItem, isLocked: Bool = false, menu: [ContextMenuItem]) {
public init(account: Account, theme: PresentationTheme, strings: PresentationStrings, item: StickerPreviewPeekItem, isLocked: Bool = false, menu: [ContextMenuItem]) {
self.account = account
self.theme = theme
self.strings = strings
self.item = item
self.isLocked = isLocked
if isLocked {
@ -68,7 +71,7 @@ public final class StickerPreviewPeekContent: PeekControllerContent {
public func fullScreenAccessoryNode() -> (PeekControllerAccessoryNode & ASDisplayNode)? {
if self.isLocked {
return PremiumStickerPackAccessoryNode(theme: self.theme)
return PremiumStickerPackAccessoryNode(theme: self.theme, strings: self.strings)
} else {
return nil
}
@ -207,18 +210,19 @@ final class PremiumStickerPackAccessoryNode: ASDisplayNode, PeekControllerAccess
let proceedButton: SolidRoundedButtonNode
let cancelButton: HighlightableButtonNode
init(theme: PresentationTheme) {
init(theme: PresentationTheme, strings: PresentationStrings) {
self.textNode = ImmediateTextNode()
self.textNode.displaysAsynchronously = false
self.textNode.textAlignment = .center
self.textNode.maximumNumberOfLines = 0
self.textNode.attributedText = NSAttributedString(string: "Unlock this sticker and more by subscribing to Telegram Premium.", font: Font.regular(17.0), textColor: .black)
self.textNode.attributedText = NSAttributedString(string: strings.Premium_Stickers_Description, font: Font.regular(17.0), textColor: .black)
self.textNode.lineSpacing = 0.1
self.textNode.alpha = 0.4
self.proceedButton = SolidRoundedButtonNode(title: "Unlock Premium Stickers", theme: SolidRoundedButtonTheme(theme: theme), height: 50.0, cornerRadius: 11.0, gloss: true)
self.proceedButton = SolidRoundedButtonNode(title: strings.Premium_Stickers_Proceed, icon: UIImage(bundleImageName: "Premium/ButtonIcon"), theme: SolidRoundedButtonTheme(theme: theme), height: 50.0, cornerRadius: 11.0, gloss: true)
self.cancelButton = HighlightableButtonNode()
self.cancelButton.setTitle("Cancel", with: Font.regular(17.0), with: theme.list.itemAccentColor, for: .normal)
self.cancelButton.setTitle(strings.Common_Cancel, with: Font.regular(17.0), with: theme.list.itemAccentColor, for: .normal)
super.init()

View File

@ -574,9 +574,9 @@ class TabBarNode: ASDisplayNode {
node.contentWidth = max(contentWidth, imageContentWidth)
node.isSelected = true
ContainedViewLayoutTransition.animated(duration: 0.35, curve: .easeInOut).updateTransformScale(node: node.ringImageNode, scale: 1.0, delay: 0.1)
ContainedViewLayoutTransition.animated(duration: 0.2, curve: .easeInOut).updateTransformScale(node: node.ringImageNode, scale: 1.0, delay: 0.1)
node.imageNode.layer.animateScale(from: 1.0, to: 0.87, duration: 0.1, removeOnCompletion: false, completion: { [weak node] _ in
node?.imageNode.layer.animateScale(from: 0.87, to: 1.0, duration: 0.35, removeOnCompletion: false, completion: { [weak node] _ in
node?.imageNode.layer.animateScale(from: 0.87, to: 1.0, duration: 0.2, removeOnCompletion: false, completion: { [weak node] _ in
node?.imageNode.layer.removeAllAnimations()
})
})

View File

@ -43,12 +43,17 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
dict[571523412] = { return $0.readDouble() }
dict[-1255641564] = { return parseString($0) }
dict[-1194283041] = { return Api.AccountDaysTTL.parse_accountDaysTTL($0) }
dict[-381896846] = { return Api.AttachMenuBot.parse_attachMenuBot($0) }
dict[-928371502] = { return Api.AttachMenuBot.parse_attachMenuBot($0) }
dict[-1297663893] = { return Api.AttachMenuBotIcon.parse_attachMenuBotIcon($0) }
dict[1165423600] = { return Api.AttachMenuBotIconColor.parse_attachMenuBotIconColor($0) }
dict[1011024320] = { return Api.AttachMenuBots.parse_attachMenuBots($0) }
dict[-237467044] = { return Api.AttachMenuBots.parse_attachMenuBotsNotModified($0) }
dict[-1816172929] = { return Api.AttachMenuBotsBot.parse_attachMenuBotsBot($0) }
dict[-1020528102] = { return Api.AttachMenuPeerType.parse_attachMenuPeerTypeBotPM($0) }
dict[2080104188] = { return Api.AttachMenuPeerType.parse_attachMenuPeerTypeBroadcast($0) }
dict[84480319] = { return Api.AttachMenuPeerType.parse_attachMenuPeerTypeChat($0) }
dict[-247016673] = { return Api.AttachMenuPeerType.parse_attachMenuPeerTypePM($0) }
dict[2104224014] = { return Api.AttachMenuPeerType.parse_attachMenuPeerTypeSameBotPM($0) }
dict[-1392388579] = { return Api.Authorization.parse_authorization($0) }
dict[-532532493] = { return Api.AutoDownloadSettings.parse_autoDownloadSettings($0) }
dict[-1065882623] = { return Api.AvailableReaction.parse_availableReaction($0) }
@ -1097,6 +1102,8 @@ public extension Api {
_1.serialize(buffer, boxed)
case let _1 as Api.AttachMenuBotsBot:
_1.serialize(buffer, boxed)
case let _1 as Api.AttachMenuPeerType:
_1.serialize(buffer, boxed)
case let _1 as Api.Authorization:
_1.serialize(buffer, boxed)
case let _1 as Api.AutoDownloadSettings:

View File

@ -36,18 +36,23 @@ public extension Api {
}
public extension Api {
enum AttachMenuBot: TypeConstructorDescription {
case attachMenuBot(flags: Int32, botId: Int64, shortName: String, icons: [Api.AttachMenuBotIcon])
case attachMenuBot(flags: Int32, botId: Int64, shortName: String, peerTypes: [Api.AttachMenuPeerType], icons: [Api.AttachMenuBotIcon])
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .attachMenuBot(let flags, let botId, let shortName, let icons):
case .attachMenuBot(let flags, let botId, let shortName, let peerTypes, let icons):
if boxed {
buffer.appendInt32(-381896846)
buffer.appendInt32(-928371502)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeInt64(botId, buffer: buffer, boxed: false)
serializeString(shortName, buffer: buffer, boxed: false)
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(peerTypes.count))
for item in peerTypes {
item.serialize(buffer, true)
}
buffer.appendInt32(481674261)
buffer.appendInt32(Int32(icons.count))
for item in icons {
item.serialize(buffer, true)
@ -58,8 +63,8 @@ public extension Api {
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .attachMenuBot(let flags, let botId, let shortName, let icons):
return ("attachMenuBot", [("flags", String(describing: flags)), ("botId", String(describing: botId)), ("shortName", String(describing: shortName)), ("icons", String(describing: icons))])
case .attachMenuBot(let flags, let botId, let shortName, let peerTypes, let icons):
return ("attachMenuBot", [("flags", String(describing: flags)), ("botId", String(describing: botId)), ("shortName", String(describing: shortName)), ("peerTypes", String(describing: peerTypes)), ("icons", String(describing: icons))])
}
}
@ -70,16 +75,21 @@ public extension Api {
_2 = reader.readInt64()
var _3: String?
_3 = parseString(reader)
var _4: [Api.AttachMenuBotIcon]?
var _4: [Api.AttachMenuPeerType]?
if let _ = reader.readInt32() {
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.AttachMenuBotIcon.self)
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.AttachMenuPeerType.self)
}
var _5: [Api.AttachMenuBotIcon]?
if let _ = reader.readInt32() {
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.AttachMenuBotIcon.self)
}
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
if _c1 && _c2 && _c3 && _c4 {
return Api.AttachMenuBot.attachMenuBot(flags: _1!, botId: _2!, shortName: _3!, icons: _4!)
let _c5 = _5 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 {
return Api.AttachMenuBot.attachMenuBot(flags: _1!, botId: _2!, shortName: _3!, peerTypes: _4!, icons: _5!)
}
else {
return nil
@ -300,6 +310,82 @@ public extension Api {
}
}
public extension Api {
enum AttachMenuPeerType: TypeConstructorDescription {
case attachMenuPeerTypeBotPM
case attachMenuPeerTypeBroadcast
case attachMenuPeerTypeChat
case attachMenuPeerTypePM
case attachMenuPeerTypeSameBotPM
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .attachMenuPeerTypeBotPM:
if boxed {
buffer.appendInt32(-1020528102)
}
break
case .attachMenuPeerTypeBroadcast:
if boxed {
buffer.appendInt32(2080104188)
}
break
case .attachMenuPeerTypeChat:
if boxed {
buffer.appendInt32(84480319)
}
break
case .attachMenuPeerTypePM:
if boxed {
buffer.appendInt32(-247016673)
}
break
case .attachMenuPeerTypeSameBotPM:
if boxed {
buffer.appendInt32(2104224014)
}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .attachMenuPeerTypeBotPM:
return ("attachMenuPeerTypeBotPM", [])
case .attachMenuPeerTypeBroadcast:
return ("attachMenuPeerTypeBroadcast", [])
case .attachMenuPeerTypeChat:
return ("attachMenuPeerTypeChat", [])
case .attachMenuPeerTypePM:
return ("attachMenuPeerTypePM", [])
case .attachMenuPeerTypeSameBotPM:
return ("attachMenuPeerTypeSameBotPM", [])
}
}
public static func parse_attachMenuPeerTypeBotPM(_ reader: BufferReader) -> AttachMenuPeerType? {
return Api.AttachMenuPeerType.attachMenuPeerTypeBotPM
}
public static func parse_attachMenuPeerTypeBroadcast(_ reader: BufferReader) -> AttachMenuPeerType? {
return Api.AttachMenuPeerType.attachMenuPeerTypeBroadcast
}
public static func parse_attachMenuPeerTypeChat(_ reader: BufferReader) -> AttachMenuPeerType? {
return Api.AttachMenuPeerType.attachMenuPeerTypeChat
}
public static func parse_attachMenuPeerTypePM(_ reader: BufferReader) -> AttachMenuPeerType? {
return Api.AttachMenuPeerType.attachMenuPeerTypePM
}
public static func parse_attachMenuPeerTypeSameBotPM(_ reader: BufferReader) -> AttachMenuPeerType? {
return Api.AttachMenuPeerType.attachMenuPeerTypeSameBotPM
}
}
}
public extension Api {
enum Authorization: TypeConstructorDescription {
case authorization(flags: Int32, hash: Int64, deviceModel: String, platform: String, systemVersion: String, apiId: Int32, appName: String, appVersion: String, dateCreated: Int32, dateActive: Int32, ip: String, country: String, region: String)
@ -926,285 +1012,3 @@ public extension Api {
}
}
public extension Api {
enum BotInlineMessage: TypeConstructorDescription {
case botInlineMessageMediaAuto(flags: Int32, message: String, entities: [Api.MessageEntity]?, replyMarkup: Api.ReplyMarkup?)
case botInlineMessageMediaContact(flags: Int32, phoneNumber: String, firstName: String, lastName: String, vcard: String, replyMarkup: Api.ReplyMarkup?)
case botInlineMessageMediaGeo(flags: Int32, geo: Api.GeoPoint, heading: Int32?, period: Int32?, proximityNotificationRadius: Int32?, replyMarkup: Api.ReplyMarkup?)
case botInlineMessageMediaInvoice(flags: Int32, title: String, description: String, photo: Api.WebDocument?, currency: String, totalAmount: Int64, replyMarkup: Api.ReplyMarkup?)
case botInlineMessageMediaVenue(flags: Int32, geo: Api.GeoPoint, title: String, address: String, provider: String, venueId: String, venueType: String, replyMarkup: Api.ReplyMarkup?)
case botInlineMessageText(flags: Int32, message: String, entities: [Api.MessageEntity]?, replyMarkup: Api.ReplyMarkup?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .botInlineMessageMediaAuto(let flags, let message, let entities, let replyMarkup):
if boxed {
buffer.appendInt32(1984755728)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeString(message, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261)
buffer.appendInt32(Int32(entities!.count))
for item in entities! {
item.serialize(buffer, true)
}}
if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)}
break
case .botInlineMessageMediaContact(let flags, let phoneNumber, let firstName, let lastName, let vcard, let replyMarkup):
if boxed {
buffer.appendInt32(416402882)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeString(phoneNumber, buffer: buffer, boxed: false)
serializeString(firstName, buffer: buffer, boxed: false)
serializeString(lastName, buffer: buffer, boxed: false)
serializeString(vcard, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)}
break
case .botInlineMessageMediaGeo(let flags, let geo, let heading, let period, let proximityNotificationRadius, let replyMarkup):
if boxed {
buffer.appendInt32(85477117)
}
serializeInt32(flags, buffer: buffer, boxed: false)
geo.serialize(buffer, true)
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(heading!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(period!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 3) != 0 {serializeInt32(proximityNotificationRadius!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)}
break
case .botInlineMessageMediaInvoice(let flags, let title, let description, let photo, let currency, let totalAmount, let replyMarkup):
if boxed {
buffer.appendInt32(894081801)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeString(title, buffer: buffer, boxed: false)
serializeString(description, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {photo!.serialize(buffer, true)}
serializeString(currency, buffer: buffer, boxed: false)
serializeInt64(totalAmount, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)}
break
case .botInlineMessageMediaVenue(let flags, let geo, let title, let address, let provider, let venueId, let venueType, let replyMarkup):
if boxed {
buffer.appendInt32(-1970903652)
}
serializeInt32(flags, buffer: buffer, boxed: false)
geo.serialize(buffer, true)
serializeString(title, buffer: buffer, boxed: false)
serializeString(address, buffer: buffer, boxed: false)
serializeString(provider, buffer: buffer, boxed: false)
serializeString(venueId, buffer: buffer, boxed: false)
serializeString(venueType, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)}
break
case .botInlineMessageText(let flags, let message, let entities, let replyMarkup):
if boxed {
buffer.appendInt32(-1937807902)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeString(message, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261)
buffer.appendInt32(Int32(entities!.count))
for item in entities! {
item.serialize(buffer, true)
}}
if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .botInlineMessageMediaAuto(let flags, let message, let entities, let replyMarkup):
return ("botInlineMessageMediaAuto", [("flags", String(describing: flags)), ("message", String(describing: message)), ("entities", String(describing: entities)), ("replyMarkup", String(describing: replyMarkup))])
case .botInlineMessageMediaContact(let flags, let phoneNumber, let firstName, let lastName, let vcard, let replyMarkup):
return ("botInlineMessageMediaContact", [("flags", String(describing: flags)), ("phoneNumber", String(describing: phoneNumber)), ("firstName", String(describing: firstName)), ("lastName", String(describing: lastName)), ("vcard", String(describing: vcard)), ("replyMarkup", String(describing: replyMarkup))])
case .botInlineMessageMediaGeo(let flags, let geo, let heading, let period, let proximityNotificationRadius, let replyMarkup):
return ("botInlineMessageMediaGeo", [("flags", String(describing: flags)), ("geo", String(describing: geo)), ("heading", String(describing: heading)), ("period", String(describing: period)), ("proximityNotificationRadius", String(describing: proximityNotificationRadius)), ("replyMarkup", String(describing: replyMarkup))])
case .botInlineMessageMediaInvoice(let flags, let title, let description, let photo, let currency, let totalAmount, let replyMarkup):
return ("botInlineMessageMediaInvoice", [("flags", String(describing: flags)), ("title", String(describing: title)), ("description", String(describing: description)), ("photo", String(describing: photo)), ("currency", String(describing: currency)), ("totalAmount", String(describing: totalAmount)), ("replyMarkup", String(describing: replyMarkup))])
case .botInlineMessageMediaVenue(let flags, let geo, let title, let address, let provider, let venueId, let venueType, let replyMarkup):
return ("botInlineMessageMediaVenue", [("flags", String(describing: flags)), ("geo", String(describing: geo)), ("title", String(describing: title)), ("address", String(describing: address)), ("provider", String(describing: provider)), ("venueId", String(describing: venueId)), ("venueType", String(describing: venueType)), ("replyMarkup", String(describing: replyMarkup))])
case .botInlineMessageText(let flags, let message, let entities, let replyMarkup):
return ("botInlineMessageText", [("flags", String(describing: flags)), ("message", String(describing: message)), ("entities", String(describing: entities)), ("replyMarkup", String(describing: replyMarkup))])
}
}
public static func parse_botInlineMessageMediaAuto(_ reader: BufferReader) -> BotInlineMessage? {
var _1: Int32?
_1 = reader.readInt32()
var _2: String?
_2 = parseString(reader)
var _3: [Api.MessageEntity]?
if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() {
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self)
} }
var _4: Api.ReplyMarkup?
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
_4 = Api.parse(reader, signature: signature) as? Api.ReplyMarkup
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil
let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil
if _c1 && _c2 && _c3 && _c4 {
return Api.BotInlineMessage.botInlineMessageMediaAuto(flags: _1!, message: _2!, entities: _3, replyMarkup: _4)
}
else {
return nil
}
}
public static func parse_botInlineMessageMediaContact(_ reader: BufferReader) -> BotInlineMessage? {
var _1: Int32?
_1 = reader.readInt32()
var _2: String?
_2 = parseString(reader)
var _3: String?
_3 = parseString(reader)
var _4: String?
_4 = parseString(reader)
var _5: String?
_5 = parseString(reader)
var _6: Api.ReplyMarkup?
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
_6 = Api.parse(reader, signature: signature) as? Api.ReplyMarkup
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
let _c5 = _5 != nil
let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
return Api.BotInlineMessage.botInlineMessageMediaContact(flags: _1!, phoneNumber: _2!, firstName: _3!, lastName: _4!, vcard: _5!, replyMarkup: _6)
}
else {
return nil
}
}
public static func parse_botInlineMessageMediaGeo(_ reader: BufferReader) -> BotInlineMessage? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Api.GeoPoint?
if let signature = reader.readInt32() {
_2 = Api.parse(reader, signature: signature) as? Api.GeoPoint
}
var _3: Int32?
if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() }
var _4: Int32?
if Int(_1!) & Int(1 << 1) != 0 {_4 = reader.readInt32() }
var _5: Int32?
if Int(_1!) & Int(1 << 3) != 0 {_5 = reader.readInt32() }
var _6: Api.ReplyMarkup?
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
_6 = Api.parse(reader, signature: signature) as? Api.ReplyMarkup
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil
let _c5 = (Int(_1!) & Int(1 << 3) == 0) || _5 != nil
let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
return Api.BotInlineMessage.botInlineMessageMediaGeo(flags: _1!, geo: _2!, heading: _3, period: _4, proximityNotificationRadius: _5, replyMarkup: _6)
}
else {
return nil
}
}
public static func parse_botInlineMessageMediaInvoice(_ reader: BufferReader) -> BotInlineMessage? {
var _1: Int32?
_1 = reader.readInt32()
var _2: String?
_2 = parseString(reader)
var _3: String?
_3 = parseString(reader)
var _4: Api.WebDocument?
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
_4 = Api.parse(reader, signature: signature) as? Api.WebDocument
} }
var _5: String?
_5 = parseString(reader)
var _6: Int64?
_6 = reader.readInt64()
var _7: Api.ReplyMarkup?
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
_7 = Api.parse(reader, signature: signature) as? Api.ReplyMarkup
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil
let _c5 = _5 != nil
let _c6 = _6 != nil
let _c7 = (Int(_1!) & Int(1 << 2) == 0) || _7 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
return Api.BotInlineMessage.botInlineMessageMediaInvoice(flags: _1!, title: _2!, description: _3!, photo: _4, currency: _5!, totalAmount: _6!, replyMarkup: _7)
}
else {
return nil
}
}
public static func parse_botInlineMessageMediaVenue(_ reader: BufferReader) -> BotInlineMessage? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Api.GeoPoint?
if let signature = reader.readInt32() {
_2 = Api.parse(reader, signature: signature) as? Api.GeoPoint
}
var _3: String?
_3 = parseString(reader)
var _4: String?
_4 = parseString(reader)
var _5: String?
_5 = parseString(reader)
var _6: String?
_6 = parseString(reader)
var _7: String?
_7 = parseString(reader)
var _8: Api.ReplyMarkup?
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
_8 = Api.parse(reader, signature: signature) as? Api.ReplyMarkup
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
let _c5 = _5 != nil
let _c6 = _6 != nil
let _c7 = _7 != nil
let _c8 = (Int(_1!) & Int(1 << 2) == 0) || _8 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 {
return Api.BotInlineMessage.botInlineMessageMediaVenue(flags: _1!, geo: _2!, title: _3!, address: _4!, provider: _5!, venueId: _6!, venueType: _7!, replyMarkup: _8)
}
else {
return nil
}
}
public static func parse_botInlineMessageText(_ reader: BufferReader) -> BotInlineMessage? {
var _1: Int32?
_1 = reader.readInt32()
var _2: String?
_2 = parseString(reader)
var _3: [Api.MessageEntity]?
if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() {
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self)
} }
var _4: Api.ReplyMarkup?
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
_4 = Api.parse(reader, signature: signature) as? Api.ReplyMarkup
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil
let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil
if _c1 && _c2 && _c3 && _c4 {
return Api.BotInlineMessage.botInlineMessageText(flags: _1!, message: _2!, entities: _3, replyMarkup: _4)
}
else {
return nil
}
}
}
}

View File

@ -1,3 +1,285 @@
public extension Api {
enum BotInlineMessage: TypeConstructorDescription {
case botInlineMessageMediaAuto(flags: Int32, message: String, entities: [Api.MessageEntity]?, replyMarkup: Api.ReplyMarkup?)
case botInlineMessageMediaContact(flags: Int32, phoneNumber: String, firstName: String, lastName: String, vcard: String, replyMarkup: Api.ReplyMarkup?)
case botInlineMessageMediaGeo(flags: Int32, geo: Api.GeoPoint, heading: Int32?, period: Int32?, proximityNotificationRadius: Int32?, replyMarkup: Api.ReplyMarkup?)
case botInlineMessageMediaInvoice(flags: Int32, title: String, description: String, photo: Api.WebDocument?, currency: String, totalAmount: Int64, replyMarkup: Api.ReplyMarkup?)
case botInlineMessageMediaVenue(flags: Int32, geo: Api.GeoPoint, title: String, address: String, provider: String, venueId: String, venueType: String, replyMarkup: Api.ReplyMarkup?)
case botInlineMessageText(flags: Int32, message: String, entities: [Api.MessageEntity]?, replyMarkup: Api.ReplyMarkup?)
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
switch self {
case .botInlineMessageMediaAuto(let flags, let message, let entities, let replyMarkup):
if boxed {
buffer.appendInt32(1984755728)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeString(message, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261)
buffer.appendInt32(Int32(entities!.count))
for item in entities! {
item.serialize(buffer, true)
}}
if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)}
break
case .botInlineMessageMediaContact(let flags, let phoneNumber, let firstName, let lastName, let vcard, let replyMarkup):
if boxed {
buffer.appendInt32(416402882)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeString(phoneNumber, buffer: buffer, boxed: false)
serializeString(firstName, buffer: buffer, boxed: false)
serializeString(lastName, buffer: buffer, boxed: false)
serializeString(vcard, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)}
break
case .botInlineMessageMediaGeo(let flags, let geo, let heading, let period, let proximityNotificationRadius, let replyMarkup):
if boxed {
buffer.appendInt32(85477117)
}
serializeInt32(flags, buffer: buffer, boxed: false)
geo.serialize(buffer, true)
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(heading!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 1) != 0 {serializeInt32(period!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 3) != 0 {serializeInt32(proximityNotificationRadius!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)}
break
case .botInlineMessageMediaInvoice(let flags, let title, let description, let photo, let currency, let totalAmount, let replyMarkup):
if boxed {
buffer.appendInt32(894081801)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeString(title, buffer: buffer, boxed: false)
serializeString(description, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {photo!.serialize(buffer, true)}
serializeString(currency, buffer: buffer, boxed: false)
serializeInt64(totalAmount, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)}
break
case .botInlineMessageMediaVenue(let flags, let geo, let title, let address, let provider, let venueId, let venueType, let replyMarkup):
if boxed {
buffer.appendInt32(-1970903652)
}
serializeInt32(flags, buffer: buffer, boxed: false)
geo.serialize(buffer, true)
serializeString(title, buffer: buffer, boxed: false)
serializeString(address, buffer: buffer, boxed: false)
serializeString(provider, buffer: buffer, boxed: false)
serializeString(venueId, buffer: buffer, boxed: false)
serializeString(venueType, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)}
break
case .botInlineMessageText(let flags, let message, let entities, let replyMarkup):
if boxed {
buffer.appendInt32(-1937807902)
}
serializeInt32(flags, buffer: buffer, boxed: false)
serializeString(message, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 1) != 0 {buffer.appendInt32(481674261)
buffer.appendInt32(Int32(entities!.count))
for item in entities! {
item.serialize(buffer, true)
}}
if Int(flags) & Int(1 << 2) != 0 {replyMarkup!.serialize(buffer, true)}
break
}
}
public func descriptionFields() -> (String, [(String, Any)]) {
switch self {
case .botInlineMessageMediaAuto(let flags, let message, let entities, let replyMarkup):
return ("botInlineMessageMediaAuto", [("flags", String(describing: flags)), ("message", String(describing: message)), ("entities", String(describing: entities)), ("replyMarkup", String(describing: replyMarkup))])
case .botInlineMessageMediaContact(let flags, let phoneNumber, let firstName, let lastName, let vcard, let replyMarkup):
return ("botInlineMessageMediaContact", [("flags", String(describing: flags)), ("phoneNumber", String(describing: phoneNumber)), ("firstName", String(describing: firstName)), ("lastName", String(describing: lastName)), ("vcard", String(describing: vcard)), ("replyMarkup", String(describing: replyMarkup))])
case .botInlineMessageMediaGeo(let flags, let geo, let heading, let period, let proximityNotificationRadius, let replyMarkup):
return ("botInlineMessageMediaGeo", [("flags", String(describing: flags)), ("geo", String(describing: geo)), ("heading", String(describing: heading)), ("period", String(describing: period)), ("proximityNotificationRadius", String(describing: proximityNotificationRadius)), ("replyMarkup", String(describing: replyMarkup))])
case .botInlineMessageMediaInvoice(let flags, let title, let description, let photo, let currency, let totalAmount, let replyMarkup):
return ("botInlineMessageMediaInvoice", [("flags", String(describing: flags)), ("title", String(describing: title)), ("description", String(describing: description)), ("photo", String(describing: photo)), ("currency", String(describing: currency)), ("totalAmount", String(describing: totalAmount)), ("replyMarkup", String(describing: replyMarkup))])
case .botInlineMessageMediaVenue(let flags, let geo, let title, let address, let provider, let venueId, let venueType, let replyMarkup):
return ("botInlineMessageMediaVenue", [("flags", String(describing: flags)), ("geo", String(describing: geo)), ("title", String(describing: title)), ("address", String(describing: address)), ("provider", String(describing: provider)), ("venueId", String(describing: venueId)), ("venueType", String(describing: venueType)), ("replyMarkup", String(describing: replyMarkup))])
case .botInlineMessageText(let flags, let message, let entities, let replyMarkup):
return ("botInlineMessageText", [("flags", String(describing: flags)), ("message", String(describing: message)), ("entities", String(describing: entities)), ("replyMarkup", String(describing: replyMarkup))])
}
}
public static func parse_botInlineMessageMediaAuto(_ reader: BufferReader) -> BotInlineMessage? {
var _1: Int32?
_1 = reader.readInt32()
var _2: String?
_2 = parseString(reader)
var _3: [Api.MessageEntity]?
if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() {
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self)
} }
var _4: Api.ReplyMarkup?
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
_4 = Api.parse(reader, signature: signature) as? Api.ReplyMarkup
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil
let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil
if _c1 && _c2 && _c3 && _c4 {
return Api.BotInlineMessage.botInlineMessageMediaAuto(flags: _1!, message: _2!, entities: _3, replyMarkup: _4)
}
else {
return nil
}
}
public static func parse_botInlineMessageMediaContact(_ reader: BufferReader) -> BotInlineMessage? {
var _1: Int32?
_1 = reader.readInt32()
var _2: String?
_2 = parseString(reader)
var _3: String?
_3 = parseString(reader)
var _4: String?
_4 = parseString(reader)
var _5: String?
_5 = parseString(reader)
var _6: Api.ReplyMarkup?
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
_6 = Api.parse(reader, signature: signature) as? Api.ReplyMarkup
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
let _c5 = _5 != nil
let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
return Api.BotInlineMessage.botInlineMessageMediaContact(flags: _1!, phoneNumber: _2!, firstName: _3!, lastName: _4!, vcard: _5!, replyMarkup: _6)
}
else {
return nil
}
}
public static func parse_botInlineMessageMediaGeo(_ reader: BufferReader) -> BotInlineMessage? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Api.GeoPoint?
if let signature = reader.readInt32() {
_2 = Api.parse(reader, signature: signature) as? Api.GeoPoint
}
var _3: Int32?
if Int(_1!) & Int(1 << 0) != 0 {_3 = reader.readInt32() }
var _4: Int32?
if Int(_1!) & Int(1 << 1) != 0 {_4 = reader.readInt32() }
var _5: Int32?
if Int(_1!) & Int(1 << 3) != 0 {_5 = reader.readInt32() }
var _6: Api.ReplyMarkup?
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
_6 = Api.parse(reader, signature: signature) as? Api.ReplyMarkup
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = (Int(_1!) & Int(1 << 0) == 0) || _3 != nil
let _c4 = (Int(_1!) & Int(1 << 1) == 0) || _4 != nil
let _c5 = (Int(_1!) & Int(1 << 3) == 0) || _5 != nil
let _c6 = (Int(_1!) & Int(1 << 2) == 0) || _6 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
return Api.BotInlineMessage.botInlineMessageMediaGeo(flags: _1!, geo: _2!, heading: _3, period: _4, proximityNotificationRadius: _5, replyMarkup: _6)
}
else {
return nil
}
}
public static func parse_botInlineMessageMediaInvoice(_ reader: BufferReader) -> BotInlineMessage? {
var _1: Int32?
_1 = reader.readInt32()
var _2: String?
_2 = parseString(reader)
var _3: String?
_3 = parseString(reader)
var _4: Api.WebDocument?
if Int(_1!) & Int(1 << 0) != 0 {if let signature = reader.readInt32() {
_4 = Api.parse(reader, signature: signature) as? Api.WebDocument
} }
var _5: String?
_5 = parseString(reader)
var _6: Int64?
_6 = reader.readInt64()
var _7: Api.ReplyMarkup?
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
_7 = Api.parse(reader, signature: signature) as? Api.ReplyMarkup
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = (Int(_1!) & Int(1 << 0) == 0) || _4 != nil
let _c5 = _5 != nil
let _c6 = _6 != nil
let _c7 = (Int(_1!) & Int(1 << 2) == 0) || _7 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
return Api.BotInlineMessage.botInlineMessageMediaInvoice(flags: _1!, title: _2!, description: _3!, photo: _4, currency: _5!, totalAmount: _6!, replyMarkup: _7)
}
else {
return nil
}
}
public static func parse_botInlineMessageMediaVenue(_ reader: BufferReader) -> BotInlineMessage? {
var _1: Int32?
_1 = reader.readInt32()
var _2: Api.GeoPoint?
if let signature = reader.readInt32() {
_2 = Api.parse(reader, signature: signature) as? Api.GeoPoint
}
var _3: String?
_3 = parseString(reader)
var _4: String?
_4 = parseString(reader)
var _5: String?
_5 = parseString(reader)
var _6: String?
_6 = parseString(reader)
var _7: String?
_7 = parseString(reader)
var _8: Api.ReplyMarkup?
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
_8 = Api.parse(reader, signature: signature) as? Api.ReplyMarkup
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = _3 != nil
let _c4 = _4 != nil
let _c5 = _5 != nil
let _c6 = _6 != nil
let _c7 = _7 != nil
let _c8 = (Int(_1!) & Int(1 << 2) == 0) || _8 != nil
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 && _c8 {
return Api.BotInlineMessage.botInlineMessageMediaVenue(flags: _1!, geo: _2!, title: _3!, address: _4!, provider: _5!, venueId: _6!, venueType: _7!, replyMarkup: _8)
}
else {
return nil
}
}
public static func parse_botInlineMessageText(_ reader: BufferReader) -> BotInlineMessage? {
var _1: Int32?
_1 = reader.readInt32()
var _2: String?
_2 = parseString(reader)
var _3: [Api.MessageEntity]?
if Int(_1!) & Int(1 << 1) != 0 {if let _ = reader.readInt32() {
_3 = Api.parseVector(reader, elementSignature: 0, elementType: Api.MessageEntity.self)
} }
var _4: Api.ReplyMarkup?
if Int(_1!) & Int(1 << 2) != 0 {if let signature = reader.readInt32() {
_4 = Api.parse(reader, signature: signature) as? Api.ReplyMarkup
} }
let _c1 = _1 != nil
let _c2 = _2 != nil
let _c3 = (Int(_1!) & Int(1 << 1) == 0) || _3 != nil
let _c4 = (Int(_1!) & Int(1 << 2) == 0) || _4 != nil
if _c1 && _c2 && _c3 && _c4 {
return Api.BotInlineMessage.botInlineMessageText(flags: _1!, message: _2!, entities: _3, replyMarkup: _4)
}
else {
return nil
}
}
}
}
public extension Api {
enum BotInlineResult: TypeConstructorDescription {
case botInlineMediaResult(flags: Int32, id: String, type: String, photo: Api.Photo?, document: Api.Document?, title: String?, description: String?, sendMessage: Api.BotInlineMessage)

View File

@ -4944,15 +4944,16 @@ public extension Api.functions.messages {
}
}
public extension Api.functions.messages {
static func prolongWebView(flags: Int32, peer: Api.InputPeer, bot: Api.InputUser, queryId: Int64, replyToMsgId: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
static func prolongWebView(flags: Int32, peer: Api.InputPeer, bot: Api.InputUser, queryId: Int64, replyToMsgId: Int32?, sendAs: Api.InputPeer?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
let buffer = Buffer()
buffer.appendInt32(-768945848)
buffer.appendInt32(-362824498)
serializeInt32(flags, buffer: buffer, boxed: false)
peer.serialize(buffer, true)
bot.serialize(buffer, true)
serializeInt64(queryId, buffer: buffer, boxed: false)
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(replyToMsgId!, buffer: buffer, boxed: false)}
return (FunctionDescription(name: "messages.prolongWebView", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("bot", String(describing: bot)), ("queryId", String(describing: queryId)), ("replyToMsgId", String(describing: replyToMsgId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
if Int(flags) & Int(1 << 13) != 0 {sendAs!.serialize(buffer, true)}
return (FunctionDescription(name: "messages.prolongWebView", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("bot", String(describing: bot)), ("queryId", String(describing: queryId)), ("replyToMsgId", String(describing: replyToMsgId)), ("sendAs", String(describing: sendAs))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
let reader = BufferReader(buffer)
var result: Api.Bool?
if let signature = reader.readInt32() {
@ -5257,9 +5258,9 @@ public extension Api.functions.messages {
}
}
public extension Api.functions.messages {
static func requestWebView(flags: Int32, peer: Api.InputPeer, bot: Api.InputUser, url: String?, startParam: String?, themeParams: Api.DataJSON?, replyToMsgId: Int32?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.WebViewResult>) {
static func requestWebView(flags: Int32, peer: Api.InputPeer, bot: Api.InputUser, url: String?, startParam: String?, themeParams: Api.DataJSON?, replyToMsgId: Int32?, sendAs: Api.InputPeer?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.WebViewResult>) {
let buffer = Buffer()
buffer.appendInt32(262163967)
buffer.appendInt32(-1850648527)
serializeInt32(flags, buffer: buffer, boxed: false)
peer.serialize(buffer, true)
bot.serialize(buffer, true)
@ -5267,7 +5268,8 @@ public extension Api.functions.messages {
if Int(flags) & Int(1 << 3) != 0 {serializeString(startParam!, buffer: buffer, boxed: false)}
if Int(flags) & Int(1 << 2) != 0 {themeParams!.serialize(buffer, true)}
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(replyToMsgId!, buffer: buffer, boxed: false)}
return (FunctionDescription(name: "messages.requestWebView", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("bot", String(describing: bot)), ("url", String(describing: url)), ("startParam", String(describing: startParam)), ("themeParams", String(describing: themeParams)), ("replyToMsgId", String(describing: replyToMsgId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.WebViewResult? in
if Int(flags) & Int(1 << 13) != 0 {sendAs!.serialize(buffer, true)}
return (FunctionDescription(name: "messages.requestWebView", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("bot", String(describing: bot)), ("url", String(describing: url)), ("startParam", String(describing: startParam)), ("themeParams", String(describing: themeParams)), ("replyToMsgId", String(describing: replyToMsgId)), ("sendAs", String(describing: sendAs))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.WebViewResult? in
let reader = BufferReader(buffer)
var result: Api.WebViewResult?
if let signature = reader.readInt32() {

View File

@ -9,6 +9,7 @@ public final class AttachMenuBots: Equatable, Codable {
case peerId
case name
case botIcons
case peerTypes
}
public enum IconName: Int32, Codable {
@ -36,6 +37,32 @@ public final class AttachMenuBots: Equatable, Codable {
}
}
public struct PeerFlags: OptionSet, Codable {
public var rawValue: UInt32
public init(rawValue: UInt32) {
self.rawValue = rawValue
}
public init() {
self.rawValue = 0
}
public static let sameBot = PeerFlags(rawValue: 1 << 0)
public static let bot = PeerFlags(rawValue: 1 << 1)
public static let user = PeerFlags(rawValue: 1 << 2)
public static let group = PeerFlags(rawValue: 1 << 3)
public static let channel = PeerFlags(rawValue: 1 << 4)
public static var all: PeerFlags {
return [.sameBot, .bot, .user, .group, .channel]
}
public static var `default`: PeerFlags {
return [.sameBot, .bot, .user]
}
}
private struct IconPair: Codable {
var name: IconName
var value: TelegramMediaFile
@ -65,15 +92,18 @@ public final class AttachMenuBots: Equatable, Codable {
public let peerId: PeerId
public let name: String
public let icons: [IconName: TelegramMediaFile]
public let peerTypes: PeerFlags
public init(
peerId: PeerId,
name: String,
icons: [IconName: TelegramMediaFile]
icons: [IconName: TelegramMediaFile],
peerTypes: PeerFlags
) {
self.peerId = peerId
self.name = name
self.icons = icons
self.peerTypes = peerTypes
}
public static func ==(lhs: Bot, rhs: Bot) -> Bool {
@ -86,6 +116,9 @@ public final class AttachMenuBots: Equatable, Codable {
if lhs.icons != rhs.icons {
return false
}
if lhs.peerTypes != rhs.peerTypes {
return false
}
return true
}
@ -103,6 +136,9 @@ public final class AttachMenuBots: Equatable, Codable {
icons[iconPair.name] = iconPair.value
}
self.icons = icons
let value = try container.decodeIfPresent(Int32.self, forKey: .peerTypes) ?? Int32(PeerFlags.default.rawValue)
self.peerTypes = PeerFlags(rawValue: UInt32(value))
}
public func encode(to encoder: Encoder) throws {
@ -116,6 +152,8 @@ public final class AttachMenuBots: Equatable, Codable {
iconPairs.append(IconPair(key, value: value))
}
try container.encode(iconPairs, forKey: .botIcons)
try container.encode(Int32(self.peerTypes.rawValue), forKey: .peerTypes)
}
}
@ -218,7 +256,7 @@ func managedSynchronizeAttachMenuBots(postbox: Postbox, network: Network, force:
var resultBots: [AttachMenuBots.Bot] = []
for bot in bots {
switch bot {
case let .attachMenuBot(_, botId, name, botIcons):
case let .attachMenuBot(_, botId, name, apiPeerTypes, botIcons):
var icons: [AttachMenuBots.Bot.IconName: TelegramMediaFile] = [:]
for icon in botIcons {
switch icon {
@ -229,7 +267,22 @@ func managedSynchronizeAttachMenuBots(postbox: Postbox, network: Network, force:
}
}
if !icons.isEmpty {
resultBots.append(AttachMenuBots.Bot(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(botId)), name: name, icons: icons))
var peerTypes: AttachMenuBots.Bot.PeerFlags = []
for apiType in apiPeerTypes {
switch apiType {
case .attachMenuPeerTypeSameBotPM:
peerTypes.insert(.sameBot)
case .attachMenuPeerTypeBotPM:
peerTypes.insert(.bot)
case .attachMenuPeerTypePM:
peerTypes.insert(.user)
case .attachMenuPeerTypeChat:
peerTypes.insert(.group)
case .attachMenuPeerTypeBroadcast:
peerTypes.insert(.channel)
}
}
resultBots.append(AttachMenuBots.Bot(peerId: PeerId(namespace: Namespaces.Peer.CloudUser, id: PeerId.Id._internalFromInt64Value(botId)), name: name, icons: icons, peerTypes: peerTypes))
}
}
}
@ -330,11 +383,13 @@ public struct AttachMenuBot {
public let peer: Peer
public let shortName: String
public let icons: [AttachMenuBots.Bot.IconName: TelegramMediaFile]
public let peerTypes: AttachMenuBots.Bot.PeerFlags
init(peer: Peer, shortName: String, icons: [AttachMenuBots.Bot.IconName: TelegramMediaFile]) {
init(peer: Peer, shortName: String, icons: [AttachMenuBots.Bot.IconName: TelegramMediaFile], peerTypes: AttachMenuBots.Bot.PeerFlags) {
self.peer = peer
self.shortName = shortName
self.icons = icons
self.peerTypes = peerTypes
}
}
@ -346,7 +401,7 @@ func _internal_attachMenuBots(postbox: Postbox) -> Signal<[AttachMenuBot], NoErr
var resultBots: [AttachMenuBot] = []
for bot in cachedBots {
if let peer = transaction.getPeer(bot.peerId) {
resultBots.append(AttachMenuBot(peer: peer, shortName: bot.name, icons: bot.icons))
resultBots.append(AttachMenuBot(peer: peer, shortName: bot.name, icons: bot.icons, peerTypes: bot.peerTypes))
}
}
return resultBots
@ -361,7 +416,7 @@ public func _internal_getAttachMenuBot(postbox: Postbox, network: Network, botId
return postbox.transaction { transaction -> Signal<AttachMenuBot, GetAttachMenuBotError> in
if cached, let cachedBots = cachedAttachMenuBots(transaction: transaction)?.bots {
if let bot = cachedBots.first(where: { $0.peerId == botId }), let peer = transaction.getPeer(bot.peerId) {
return .single(AttachMenuBot(peer: peer, shortName: bot.name, icons: bot.icons))
return .single(AttachMenuBot(peer: peer, shortName: bot.name, icons: bot.icons, peerTypes: bot.peerTypes))
}
}
@ -395,7 +450,7 @@ public func _internal_getAttachMenuBot(postbox: Postbox, network: Network, botId
}
switch bot {
case let .attachMenuBot(_, _, name, botIcons):
case let .attachMenuBot(_, _, name, apiPeerTypes, botIcons):
var icons: [AttachMenuBots.Bot.IconName: TelegramMediaFile] = [:]
for icon in botIcons {
switch icon {
@ -405,7 +460,22 @@ public func _internal_getAttachMenuBot(postbox: Postbox, network: Network, botId
}
}
}
return .single(AttachMenuBot(peer: peer, shortName: name, icons: icons))
var peerTypes: AttachMenuBots.Bot.PeerFlags = []
for apiType in apiPeerTypes {
switch apiType {
case .attachMenuPeerTypeSameBotPM:
peerTypes.insert(.sameBot)
case .attachMenuPeerTypeBotPM:
peerTypes.insert(.bot)
case .attachMenuPeerTypePM:
peerTypes.insert(.user)
case .attachMenuPeerTypeChat:
peerTypes.insert(.group)
case .attachMenuPeerTypeBroadcast:
peerTypes.insert(.channel)
}
}
return .single(AttachMenuBot(peer: peer, shortName: name, icons: icons, peerTypes: peerTypes))
}
}
}

View File

@ -51,10 +51,10 @@ public enum RequestWebViewError {
case generic
}
private func keepWebViewSignal(network: Network, stateManager: AccountStateManager, flags: Int32, peer: Api.InputPeer, bot: Api.InputUser, queryId: Int64, replyToMessageId: MessageId?) -> Signal<Never, KeepWebViewError> {
private func keepWebViewSignal(network: Network, stateManager: AccountStateManager, flags: Int32, peer: Api.InputPeer, bot: Api.InputUser, queryId: Int64, replyToMessageId: MessageId?, sendAs: Api.InputPeer?) -> Signal<Never, KeepWebViewError> {
let signal = Signal<Never, KeepWebViewError> { subscriber in
let poll = Signal<Never, KeepWebViewError> { subscriber in
let signal: Signal<Never, KeepWebViewError> = network.request(Api.functions.messages.prolongWebView(flags: flags, peer: peer, bot: bot, queryId: queryId, replyToMsgId: replyToMessageId?.id))
let signal: Signal<Never, KeepWebViewError> = network.request(Api.functions.messages.prolongWebView(flags: flags, peer: peer, bot: bot, queryId: queryId, replyToMsgId: replyToMessageId?.id, sendAs: sendAs))
|> mapError { _ -> KeepWebViewError in
return .generic
}
@ -122,14 +122,17 @@ func _internal_requestWebView(postbox: Postbox, network: Network, stateManager:
if fromMenu {
flags |= (1 << 4)
}
return network.request(Api.functions.messages.requestWebView(flags: flags, peer: inputPeer, bot: inputBot, url: url, startParam: payload, themeParams: serializedThemeParams, replyToMsgId: replyToMsgId))
// if _ {
// flags |= (1 << 13)
// }
return network.request(Api.functions.messages.requestWebView(flags: flags, peer: inputPeer, bot: inputBot, url: url, startParam: payload, themeParams: serializedThemeParams, replyToMsgId: replyToMsgId, sendAs: nil))
|> mapError { _ -> RequestWebViewError in
return .generic
}
|> mapToSignal { result -> Signal<RequestWebViewResult, RequestWebViewError> in
switch result {
case let .webViewResultUrl(queryId, url):
return .single(RequestWebViewResult(queryId: queryId, url: url, keepAliveSignal: keepWebViewSignal(network: network, stateManager: stateManager, flags: flags, peer: inputPeer, bot: inputBot, queryId: queryId, replyToMessageId: replyToMessageId)))
return .single(RequestWebViewResult(queryId: queryId, url: url, keepAliveSignal: keepWebViewSignal(network: network, stateManager: stateManager, flags: flags, peer: inputPeer, bot: inputBot, queryId: queryId, replyToMessageId: replyToMessageId, sendAs: nil)))
}
}
}

View File

@ -267,6 +267,7 @@ swift_library(
"//submodules/PeerInfoUI/CreateExternalMediaStreamScreen:CreateExternalMediaStreamScreen",
"//submodules/TranslateUI:TranslateUI",
"//submodules/BrowserUI:BrowserUI",
"//submodules/PremiumUI:PremiumUI",
"//submodules/Components/HierarchyTrackingLayer:HierarchyTrackingLayer",
] + select({
"@build_bazel_rules_apple//apple:ios_armv7": [],

View File

@ -1,6 +1,6 @@
{
"info" : {
"version" : 1,
"author" : "xcode"
"author" : "xcode",
"version" : 1
}
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "premium_24 (2).pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,97 @@
%PDF-1.7
1 0 obj
<< >>
endobj
2 0 obj
<< /Length 3 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 1.565002 2.396484 cm
0.000000 0.000000 0.000000 scn
9.882594 3.317978 m
5.183870 0.439518 l
4.695292 0.140213 4.056584 0.293650 3.757278 0.782228 c
3.611097 1.020851 3.567514 1.308409 3.636422 1.579630 c
4.363782 4.442544 l
4.626348 5.476007 5.333546 6.339901 6.294793 6.801413 c
11.420867 9.262531 l
11.659847 9.377270 11.760565 9.664015 11.645826 9.902994 c
11.552906 10.096530 11.342772 10.204829 11.131232 10.168206 c
5.425254 9.180357 l
4.265362 8.979550 3.075904 9.299845 2.173681 10.055932 c
0.371115 11.566533 l
-0.068036 11.934555 -0.125698 12.588898 0.242323 13.028049 c
0.421316 13.241636 0.678710 13.374235 0.956533 13.395977 c
6.463908 13.826983 l
6.852989 13.857433 7.192064 14.103661 7.341429 14.464218 c
9.466071 19.592974 l
9.685358 20.122318 10.292244 20.373671 10.821589 20.154385 c
11.075764 20.049089 11.277705 19.847147 11.382999 19.592974 c
13.507642 14.464218 l
13.657006 14.103661 13.996081 13.857433 14.385162 13.826983 c
19.922798 13.393608 l
20.494020 13.348906 20.920849 12.849598 20.876144 12.278376 c
20.854639 12.003585 20.724667 11.748647 20.514914 11.569828 c
16.291594 7.969350 l
15.994287 7.715888 15.864580 7.316894 15.956014 6.937058 c
17.254391 1.543314 l
17.388485 0.986258 17.045605 0.425970 16.488548 0.291876 c
16.220879 0.227442 15.938575 0.272047 15.703809 0.415865 c
10.966476 3.317978 l
10.633904 3.521713 10.215167 3.521713 9.882594 3.317978 c
h
f*
n
Q
endstream
endobj
3 0 obj
1468
endobj
4 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 24.000000 24.000000 ]
/Resources 1 0 R
/Contents 2 0 R
/Parent 5 0 R
>>
endobj
5 0 obj
<< /Kids [ 4 0 R ]
/Count 1
/Type /Pages
>>
endobj
6 0 obj
<< /Pages 5 0 R
/Type /Catalog
>>
endobj
xref
0 7
0000000000 65535 f
0000000010 00000 n
0000000034 00000 n
0000001558 00000 n
0000001581 00000 n
0000001754 00000 n
0000001828 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 6 0 R
/Size 7
>>
startxref
1887
%%EOF

View File

@ -0,0 +1,9 @@
{
"info" : {
"author" : "xcode",
"version" : 1
},
"properties" : {
"provides-namespace" : true
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "1.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,12 @@
{
"images" : [
{
"filename" : "addreactions_32.pdf",
"idiom" : "universal"
}
],
"info" : {
"author" : "xcode",
"version" : 1
}
}

View File

@ -0,0 +1,201 @@
%PDF-1.7
1 0 obj
<< /Length 2 0 R
/Range [ 0.000000 1.000000 0.000000 1.000000 0.000000 1.000000 ]
/Domain [ 0.000000 1.000000 ]
/FunctionType 4
>>
stream
{ 0.000000 exch 0.721569 exch 1.000000 exch dup 0.000000 gt { exch pop exch pop exch pop dup 0.000000 sub 0.000000 mul 0.000000 add exch dup 0.000000 sub -0.243137 mul 0.721569 add exch dup 0.000000 sub 0.000000 mul 1.000000 add exch } if dup 1.000000 gt { exch pop exch pop exch pop 0.000000 exch 0.478431 exch 1.000000 exch } if pop }
endstream
endobj
2 0 obj
337
endobj
3 0 obj
<< /Pattern << /P1 << /Matrix [ 0.000000 -32.000000 32.000000 0.000000 -32.000000 32.000000 ]
/Shading << /Coords [ 0.000000 0.000000 1.000000 0.000000 ]
/ColorSpace /DeviceRGB
/Function 1 0 R
/Domain [ 0.000000 1.000000 ]
/ShadingType 2
/Extend [ true true ]
>>
/PatternType 2
/Type /Pattern
>> >> >>
endobj
4 0 obj
<< /Length 5 0 R >>
stream
/DeviceRGB CS
/DeviceRGB cs
q
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
1.000000 1.000000 1.000000 scn
0.000000 16.000000 m
0.000000 24.836555 7.163444 32.000000 16.000000 32.000000 c
16.000000 32.000000 l
24.836555 32.000000 32.000000 24.836555 32.000000 16.000000 c
32.000000 16.000000 l
32.000000 7.163445 24.836555 0.000000 16.000000 0.000000 c
16.000000 0.000000 l
7.163444 0.000000 0.000000 7.163445 0.000000 16.000000 c
0.000000 16.000000 l
h
f
n
Q
q
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
/Pattern cs
/P1 scn
16.000000 0.000000 m
17.874004 0.000000 19.672758 0.322180 21.343897 0.914169 c
21.572962 0.677401 21.840872 0.478514 22.138031 0.327103 c
22.516409 0.134308 22.909628 0.062992 23.304974 0.030691 c
23.679550 0.000088 24.134321 0.000103 24.661480 0.000122 c
27.338524 0.000122 l
27.865683 0.000103 28.320454 0.000088 28.695030 0.030691 c
29.090376 0.062992 29.483595 0.134308 29.861973 0.327103 c
30.426460 0.614723 30.885403 1.073666 31.173021 1.638149 c
31.365814 2.016529 31.437132 2.409748 31.469433 2.805094 c
31.500034 3.179638 31.500019 3.634357 31.500002 4.161457 c
31.500002 4.161480 l
31.500002 4.161600 l
31.500002 4.838646 l
31.500002 4.838766 l
31.500002 4.838789 l
31.500019 5.365887 31.500034 5.820606 31.469433 6.195150 c
31.437132 6.590496 31.365814 6.983715 31.173021 7.362093 c
30.938610 7.822155 30.590397 8.212109 30.163998 8.496347 c
30.163998 8.551201 l
31.336399 10.775940 32.000000 13.310474 32.000000 16.000000 c
32.000000 24.836555 24.836555 32.000000 16.000000 32.000000 c
7.163444 32.000000 0.000000 24.836555 0.000000 16.000000 c
0.000000 7.163445 7.163444 0.000000 16.000000 0.000000 c
h
16.562183 8.757324 m
20.546453 6.359240 l
20.584953 6.699120 20.660887 7.036112 20.826982 7.362093 c
21.061394 7.822151 21.409601 8.212103 21.835997 8.496340 c
21.835997 9.500063 l
21.835997 10.039848 21.938705 10.555647 22.125647 11.028978 c
21.774387 12.530510 l
21.686312 12.907011 21.814457 13.301291 22.107056 13.554068 c
26.503191 17.351892 l
27.199739 17.953642 26.828377 19.096992 25.911175 19.174591 c
20.119438 19.664600 l
19.735479 19.697086 19.400976 19.939270 19.250256 20.293901 c
16.982876 25.628805 l
16.624168 26.472807 15.427922 26.472807 15.069214 25.628805 c
12.801836 20.293903 l
12.651114 19.939270 12.316612 19.697086 11.932652 19.664600 c
6.140916 19.174591 l
5.223714 19.096992 4.852351 17.953640 5.548900 17.351891 c
7.200050 15.925461 l
7.880803 15.337358 8.782333 15.071831 9.673182 15.197048 c
19.282505 16.547745 l
19.848860 16.627354 20.083559 15.847515 19.567341 15.601313 c
11.272570 11.645247 l
10.417443 11.237408 9.794044 10.463003 9.578240 9.540506 c
8.956986 6.884823 l
8.747575 5.989647 9.717782 5.283152 10.505456 5.757242 c
15.489906 8.757324 l
15.819762 8.955860 16.232327 8.955860 16.562183 8.757324 c
h
22.579525 1.224531 m
22.517185 1.256849 22.456810 1.292316 22.398592 1.330725 c
22.110716 1.520653 21.875710 1.782589 21.717987 2.092140 c
21.500000 2.519964 21.500000 3.080017 21.500000 4.200123 c
21.500000 4.800121 l
21.500000 5.187914 21.500002 5.508577 21.509048 5.779865 c
21.523994 6.228132 21.563643 6.541590 21.668795 6.800331 c
21.683817 6.837294 21.700174 6.873140 21.717989 6.908104 c
21.909735 7.284428 22.215696 7.590389 22.592020 7.782135 c
22.663559 7.818586 22.738798 7.848942 22.819733 7.874222 c
22.825129 7.875908 22.830549 7.877571 22.835997 7.879211 c
22.835997 7.992439 l
22.835995 9.500063 l
22.835995 11.247492 24.252565 12.664062 25.999994 12.664062 c
27.747423 12.664062 29.163994 11.247492 29.163994 9.500063 c
29.163994 7.879215 l
29.251150 7.852966 29.331669 7.821018 29.407982 7.782135 c
29.490490 7.740095 29.569614 7.692564 29.644896 7.640007 c
29.912977 7.452845 30.132307 7.201921 30.282015 6.908104 c
30.500002 6.480280 30.500002 5.920227 30.500002 4.800121 c
30.500002 4.200123 l
30.500002 3.080017 30.500002 2.519964 30.282015 2.092140 c
30.258047 2.045099 30.232294 1.999159 30.204842 1.954403 c
30.012678 1.641111 29.737267 1.385887 29.407982 1.218109 c
28.980160 1.000122 28.420107 1.000122 27.300003 1.000122 c
24.700001 1.000122 l
23.579897 1.000122 23.019844 1.000122 22.592020 1.218109 c
22.587847 1.220236 22.583681 1.222376 22.579525 1.224531 c
h
25.999994 11.336063 m
24.986000 11.336063 24.163994 10.514057 24.163994 9.500063 c
24.163994 7.998974 l
24.327213 8.000122 24.505131 8.000122 24.700001 8.000122 c
27.299999 8.000122 l
27.494865 8.000122 27.672779 8.000122 27.835995 7.998974 c
27.835995 9.500063 l
27.835995 10.514057 27.013988 11.336063 25.999994 11.336063 c
h
f*
n
Q
endstream
endobj
5 0 obj
4545
endobj
6 0 obj
<< /Annots []
/Type /Page
/MediaBox [ 0.000000 0.000000 32.000000 32.000000 ]
/Resources 3 0 R
/Contents 4 0 R
/Parent 7 0 R
>>
endobj
7 0 obj
<< /Kids [ 6 0 R ]
/Count 1
/Type /Pages
>>
endobj
8 0 obj
<< /Pages 7 0 R
/Type /Catalog
>>
endobj
xref
0 9
0000000000 65535 f
0000000010 00000 n
0000000531 00000 n
0000000553 00000 n
0000001179 00000 n
0000005780 00000 n
0000005803 00000 n
0000005976 00000 n
0000006050 00000 n
trailer
<< /ID [ (some) (id) ]
/Root 8 0 R
/Size 9
>>
startxref
6109
%%EOF

View File

@ -89,7 +89,7 @@ final class AttachmentFileEmptyStateItemNode: ItemListControllerEmptyStateItemNo
case let .bannedSendMedia(banDescription):
text = banDescription
}
self.textNode.attributedText = NSAttributedString(string: text, font: Font.regular(15.0), textColor: theme.list.freeTextColor, paragraphAlignment: .center)
self.textNode.attributedText = NSAttributedString(string: text.replacingOccurrences(of: "\n", with: " "), font: Font.regular(15.0), textColor: theme.list.freeTextColor, paragraphAlignment: .center)
}
override func updateLayout(layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) {
@ -98,7 +98,12 @@ final class AttachmentFileEmptyStateItemNode: ItemListControllerEmptyStateItemNo
insets.top += navigationBarHeight
let imageSpacing: CGFloat = 12.0
let imageSize = CGSize(width: 144.0, height: 144.0)
var imageSize = CGSize(width: 144.0, height: 144.0)
if layout.size.width == 320.0 {
imageSize = CGSize(width: 112.0, height: 112.0)
}
let imageHeight = layout.size.width < layout.size.height ? imageSize.height + imageSpacing : 0.0
if !imageHeight.isZero {
if case .intro = self.item.content {
@ -106,7 +111,11 @@ final class AttachmentFileEmptyStateItemNode: ItemListControllerEmptyStateItemNo
} else {
insets.top -= 160.0
}
}
}
if layout.size.width == 320.0 {
insets.top += 110.0
}
let textSize = self.textNode.measure(CGSize(width: layout.size.width - layout.safeInsets.left - layout.safeInsets.right - 70.0, height: max(1.0, layout.size.height - insets.top - insets.bottom)))

View File

@ -105,7 +105,6 @@ final class AudioWaveformNode: ASDisplayNode {
}
}
let invScale = 1.0 / max(1.0, CGFloat(maxSample))
let numSamples = Int(floor(size.width / (sampleWidth + distance)))
let adjustedSamplesMemory = malloc(numSamples * 2)!
@ -115,14 +114,36 @@ final class AudioWaveformNode: ASDisplayNode {
}
memset(adjustedSamplesMemory, 0, numSamples * 2)
var generateFakeSamples = false
var minSample: UInt16 = maxSample
for i in 0 ..< maxReadSamples {
let index = i * numSamples / maxReadSamples
let sample = samples[i]
if adjustedSamples[index] < sample {
adjustedSamples[index] = sample
}
if sample < minSample {
minSample = sample
}
}
if maxSample - minSample < 3 {
generateFakeSamples = true
}
if generateFakeSamples {
if maxSample < 10 {
maxSample = 20
}
for i in 0 ..< maxReadSamples {
let index = i * numSamples / maxReadSamples
adjustedSamples[index] = UInt16.random(in: 5...maxSample)
}
}
let invScale = 1.0 / max(1.0, CGFloat(maxSample))
for i in 0 ..< numSamples {
let offset = CGFloat(i) * (sampleWidth + distance)
let peakSample = adjustedSamples[i]

View File

@ -76,6 +76,7 @@ import Pasteboard
import ChatSendMessageActionUI
import ChatTextLinkEditUI
import WebUI
import PremiumUI
#if DEBUG
import os.signpost
@ -970,16 +971,22 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
let _ = combineLatest(queue: .mainQueue(),
strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: strongSelf.context.account.peerId)),
contextMenuForChatPresentationInterfaceState(chatPresentationInterfaceState: strongSelf.presentationInterfaceState, context: strongSelf.context, messages: updatedMessages, controllerInteraction: strongSelf.controllerInteraction, selectAll: selectAll, interfaceInteraction: strongSelf.interfaceInteraction),
strongSelf.context.engine.stickers.availableReactions(),
peerAllowedReactions(context: strongSelf.context, peerId: topMessage.id.peerId),
ApplicationSpecificNotice.getChatTextSelectionTips(accountManager: strongSelf.context.sharedContext.accountManager)
).start(next: { actions, availableReactions, allowedReactions, chatTextSelectionTips in
var actions = actions
).start(next: { peer, actions, availableReactions, allowedReactions, chatTextSelectionTips in
guard let strongSelf = self else {
return
}
var hasPremium = false
if case let .user(user) = peer, user.flags.contains(.isPremium) {
hasPremium = true
}
var actions = actions
switch actions.content {
case let .list(itemList):
if itemList.isEmpty {
@ -1024,17 +1031,27 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
actions.context = strongSelf.context
var premiumReactions: [AvailableReactions.Reaction] = []
if canAddMessageReactions(message: topMessage), let availableReactions = availableReactions, let allowedReactions = allowedReactions {
var hasPremiumPlaceholder = false
filterReactions: for reaction in availableReactions.reactions {
if !reaction.isEnabled {
continue
}
guard let centerAnimation = reaction.centerAnimation else {
continue
}
guard let aroundAnimation = reaction.aroundAnimation else {
continue
}
// if reaction.isPremium {
premiumReactions.append(reaction)
// }
if !reaction.isEnabled {
continue
}
if reaction.isPremium && !hasPremium {
hasPremiumPlaceholder = true
continue
}
switch allowedReactions {
case let .set(set):
@ -1054,6 +1071,9 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
largeApplicationAnimation: reaction.effectAnimation
)))
}
if hasPremiumPlaceholder {
actions.reactionItems.append(.premium)
}
}
strongSelf.chatDisplayNode.messageTransitionNode.dismissMessageReactionContexts()
@ -1062,7 +1082,19 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
strongSelf.currentContextController = controller
controller.reactionSelected = { [weak controller] value, isLarge in
guard let strongSelf = self, let message = messages.first, let reaction = value.reaction else {
guard let strongSelf = self else {
return
}
if case .premium = value {
controller?.dismiss()
let controller = PremiumStickersScreen(context: strongSelf.context, updatedPresentationData: strongSelf.updatedPresentationData, reactions: premiumReactions)
strongSelf.present(controller, in: .window(.root))
return
}
guard let message = messages.first, let reaction = value.reaction else {
return
}
@ -10710,7 +10742,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
}
let buttons: Signal<([AttachmentButtonType], AttachmentButtonType?), NoError>
if let _ = peer as? TelegramUser, !isScheduledMessages {
if !isScheduledMessages {
buttons = self.context.engine.messages.attachMenuBots()
|> map { attachMenuBots in
var buttons = availableButtons
@ -10718,12 +10750,37 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
if botId == nil {
initialButton = .gallery
}
var peerType: AttachMenuBots.Bot.PeerFlags = []
if let user = peer as? TelegramUser {
if let _ = user.botInfo {
peerType.insert(.bot)
} else {
peerType.insert(.user)
}
} else if let _ = peer as? TelegramGroup {
peerType = .group
} else if let channel = peer as? TelegramChannel {
if case .broadcast = channel.info {
peerType = .channel
} else {
peerType = .group
}
}
for bot in attachMenuBots.reversed() {
let button: AttachmentButtonType = .app(bot.peer, bot.shortName, bot.icons)
buttons.insert(button, at: 1)
var peerType = peerType
if bot.peer.id == peer.id {
peerType.insert(.sameBot)
}
if initialButton == nil && bot.peer.id == botId {
initialButton = button
if !bot.peerTypes.intersection(peerType).isEmpty {
let button: AttachmentButtonType = .app(bot.peer, bot.shortName, bot.icons)
buttons.insert(button, at: 1)
if initialButton == nil && bot.peer.id == botId {
initialButton = button
}
}
}
return (buttons, initialButton)

View File

@ -1623,7 +1623,7 @@ final class ChatMediaInputNode: ChatInputNode {
}
}
})))
return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.theme, item: item, menu: menuItems))
return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.theme, strings: strongSelf.strings, item: item, menu: menuItems))
} else {
return nil
}
@ -1655,11 +1655,17 @@ final class ChatMediaInputNode: ChatInputNode {
}
if let (itemNode, item) = itemNodeAndItem {
return strongSelf.context.account.postbox.transaction { transaction -> Bool in
return getIsStickerSaved(transaction: transaction, fileId: item.file.fileId)
let accountPeerId = strongSelf.context.account.peerId
return strongSelf.context.account.postbox.transaction { transaction -> (Bool, Bool) in
let isStarred = getIsStickerSaved(transaction: transaction, fileId: item.file.fileId)
var hasPremium = false
if let peer = transaction.getPeer(accountPeerId) as? TelegramUser, peer.flags.contains(.isPremium) {
hasPremium = true
}
return (isStarred, hasPremium)
}
|> deliverOnMainQueue
|> map { isStarred -> (ASDisplayNode, PeekControllerContent)? in
|> map { isStarred, hasPremium -> (ASDisplayNode, PeekControllerContent)? in
if let strongSelf = self {
var menuItems: [ContextMenuItem] = []
if let (_, _, _, _, _, _, _, _, interfaceState, _, _) = strongSelf.validLayout {
@ -1743,7 +1749,7 @@ final class ChatMediaInputNode: ChatInputNode {
}
}))
)
return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.theme, item: .pack(item), menu: menuItems))
return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.theme, strings: strongSelf.strings, item: .pack(item), isLocked: item.file.isPremiumSticker && !hasPremium, menu: menuItems))
} else {
return nil
}
@ -1779,6 +1785,25 @@ final class ChatMediaInputNode: ChatInputNode {
strongSelf.updatePreviewingItem(item: item, animated: true)
}
})
peekRecognizer.checkSingleTapActivationAtPoint = { [weak self] point in
guard let strongSelf = self else {
return false
}
let pane = strongSelf.stickerPane
let panelPoint = strongSelf.view.convert(point, to: strongSelf.collectionListPanel.view)
if panelPoint.y < strongSelf.collectionListPanel.frame.maxY {
return false
}
if pane.supernode != nil, pane.frame.contains(point) {
let itemNodeAndItem = pane.itemAt(point: point.offsetBy(dx: -pane.frame.minX, dy: -pane.frame.minY))
if let item = itemNodeAndItem?.0 as? ChatMediaInputStickerGridItemNode, item.isLocked == true {
return true
}
}
return false
}
self.view.addGestureRecognizer(peekRecognizer)
let panRecognizer = UIPanGestureRecognizer(target: self, action: #selector(self.panGesture(_:)))
self.panRecognizer = panRecognizer

View File

@ -178,10 +178,8 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode {
private(set) var animationNode: AnimatedStickerNode?
private(set) var placeholderNode: StickerShimmerEffectNode?
private var lockBackground: UIVisualEffectView?
private var lockTintView: UIView?
private var lockIconNode: ASImageNode?
private var isLocked: Bool?
private var lockBackground: UIImageView?
var isLocked: Bool?
private var didSetUpAnimationNode = false
private var item: ChatMediaInputStickerGridItem?
@ -315,43 +313,21 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode {
self.isLocked = item.isLocked
if item.isLocked {
let lockBackground: UIVisualEffectView
let lockIconNode: ASImageNode
if let currentBackground = self.lockBackground, let currentIcon = self.lockIconNode {
let lockBackground: UIImageView
if let currentBackground = self.lockBackground {
lockBackground = currentBackground
lockIconNode = currentIcon
} else {
let effect: UIBlurEffect
if #available(iOS 10.0, *) {
effect = UIBlurEffect(style: .regular)
} else {
effect = UIBlurEffect(style: .light)
}
lockBackground = UIVisualEffectView(effect: effect)
lockBackground = UIImageView()
lockBackground.clipsToBounds = true
lockBackground.isUserInteractionEnabled = false
lockIconNode = ASImageNode()
lockIconNode.displaysAsynchronously = false
lockIconNode.image = generateTintedImage(image: UIImage(bundleImageName: "Chat/Stickers/SmallLock"), color: .white)
let lockTintView = UIView()
lockTintView.backgroundColor = UIColor(rgb: 0x000000, alpha: 0.15)
lockBackground.contentView.addSubview(lockTintView)
lockBackground.image = UIImage(bundleImageName: "Premium/StickerIcon")
self.lockBackground = lockBackground
self.lockTintView = lockTintView
self.lockIconNode = lockIconNode
self.view.addSubview(lockBackground)
self.addSubnode(lockIconNode)
}
} else if let lockBackground = self.lockBackground, let lockTintView = self.lockTintView, let lockIconNode = self.lockIconNode {
} else if let lockBackground = self.lockBackground {
self.lockBackground = nil
self.lockTintView = nil
self.lockIconNode = nil
lockBackground.removeFromSuperview()
lockTintView.removeFromSuperview()
lockIconNode.removeFromSupernode()
}
}
@ -384,18 +360,10 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode {
placeholderNode.update(backgroundColor: theme.chat.inputMediaPanel.stickersBackgroundColor.withAlphaComponent(1.0), foregroundColor: theme.chat.inputMediaPanel.stickersSectionTextColor.blitOver(theme.chat.inputMediaPanel.stickersBackgroundColor, alpha: 0.15), shimmeringColor: theme.list.itemBlocksBackgroundColor.withAlphaComponent(0.3), data: item.stickerItem.file.immediateThumbnailData, size: placeholderFrame.size)
}
if let lockBackground = self.lockBackground, let lockTintView = self.lockTintView, let lockIconNode = self.lockIconNode {
let lockSize = CGSize(width: 24.0, height: 24.0)
if let lockBackground = self.lockBackground {
let lockSize = CGSize(width: 26.0, height: 26.0)
let lockBackgroundFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((size.width - lockSize.width) / 2.0), y: size.height - lockSize.height - 2.0), size: lockSize)
lockBackground.frame = lockBackgroundFrame
lockBackground.layer.cornerRadius = lockSize.width / 2.0
if #available(iOS 13.0, *) {
lockBackground.layer.cornerCurve = .circular
}
lockTintView.frame = CGRect(origin: CGPoint(), size: lockBackgroundFrame.size)
if let icon = lockIconNode.image {
lockIconNode.frame = CGRect(origin: CGPoint(x: lockBackgroundFrame.minX + floorToScreenPixels((lockBackgroundFrame.width - icon.size.width) / 2.0), y: lockBackgroundFrame.minY + floorToScreenPixels((lockBackgroundFrame.height - icon.size.height) / 2.0)), size: icon.size)
}
}
}
@ -410,8 +378,11 @@ final class ChatMediaInputStickerGridItemNode: GridItemNode {
return
}
if let interfaceInteraction = self.interfaceInteraction, let (_, item, _) = self.currentState, case .ended = recognizer.state {
let _ = interfaceInteraction.sendSticker(.standalone(media: item.file), false, false, nil, false, self, self.bounds)
self.imageNode.layer.animateAlpha(from: 0.5, to: 1.0, duration: 1.0)
if let isLocked = self.isLocked, isLocked {
} else {
let _ = interfaceInteraction.sendSticker(.standalone(media: item.file), false, false, nil, false, self, self.bounds)
self.imageNode.layer.animateAlpha(from: 0.5, to: 1.0, duration: 1.0)
}
}
}

View File

@ -928,7 +928,7 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
})
self.menuButtonTextNode.view.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
}
menuTextSize = self.menuButtonTextNode.updateLayout(CGSize(width: width, height: 44.0))
menuTextSize = self.menuButtonTextNode.updateLayout(CGSize(width: width / 2.0 - 60.0, height: 44.0))
var updateSendButtonIcon = false
if (previousState?.interfaceState.editMessage != nil) != (interfaceState.interfaceState.editMessage != nil) {
@ -1687,12 +1687,6 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
self.slowmodePlaceholderNode?.isHidden = true
}
transition.updateFrame(node: self.textPlaceholderNode, frame: CGRect(origin: CGPoint(x: leftInset + textFieldInsets.left + self.textInputViewInternalInsets.left, y: textFieldInsets.top + self.textInputViewInternalInsets.top + textInputViewRealInsets.top + UIScreenPixel), size: self.textPlaceholderNode.frame.size))
transition.updateAlpha(node: self.textPlaceholderNode, alpha: audioRecordingItemsAlpha)
transition.updateFrame(layer: self.textInputBackgroundNode.layer, frame: CGRect(x: leftInset + textFieldInsets.left, y: textFieldInsets.top, width: baseWidth - textFieldInsets.left - textFieldInsets.right + textInputBackgroundWidthOffset, height: panelHeight - textFieldInsets.top - textFieldInsets.bottom))
transition.updateAlpha(node: self.textInputBackgroundNode, alpha: audioRecordingItemsAlpha)
var nextButtonTopRight = CGPoint(x: width - rightInset - textFieldInsets.right - accessoryButtonInset, y: panelHeight - textFieldInsets.bottom - minimalInputHeight)
for (_, button) in self.accessoryItemButtons.reversed() {
let buttonSize = CGSize(width: button.buttonWidth, height: minimalInputHeight)
@ -1713,6 +1707,18 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
nextButtonTopRight.x -= accessoryButtonSpacing
}
let textInputBackgroundFrame = CGRect(x: leftInset + textFieldInsets.left, y: textFieldInsets.top, width: baseWidth - textFieldInsets.left - textFieldInsets.right + textInputBackgroundWidthOffset, height: panelHeight - textFieldInsets.top - textFieldInsets.bottom)
transition.updateFrame(layer: self.textInputBackgroundNode.layer, frame: textInputBackgroundFrame)
transition.updateAlpha(node: self.textInputBackgroundNode, alpha: audioRecordingItemsAlpha)
transition.updateFrame(node: self.textPlaceholderNode, frame: CGRect(origin: CGPoint(x: leftInset + textFieldInsets.left + self.textInputViewInternalInsets.left, y: textFieldInsets.top + self.textInputViewInternalInsets.top + textInputViewRealInsets.top + UIScreenPixel), size: self.textPlaceholderNode.frame.size))
var textPlaceholderAlpha: CGFloat = audioRecordingItemsAlpha
if self.textPlaceholderNode.frame.width > (nextButtonTopRight.x - textInputBackgroundFrame.minX) - 32.0 {
textPlaceholderAlpha = 0.0
}
transition.updateAlpha(node: self.textPlaceholderNode, alpha: textPlaceholderAlpha)
if let removeAccessoryButtons = removeAccessoryButtons {
for button in removeAccessoryButtons {
let buttonFrame = CGRect(origin: CGPoint(x: button.frame.origin.x + additionalOffset, y: panelHeight - textFieldInsets.bottom - minimalInputHeight), size: button.frame.size)

View File

@ -531,7 +531,7 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode {
}
}))
]
return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, item: item, menu: menuItems))
return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, item: item, menu: menuItems))
} else {
return nil
}
@ -595,7 +595,7 @@ private final class FeaturedStickersScreenNode: ViewControllerTracingNode {
}
}))
]
return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, item: .pack(item), menu: menuItems))
return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.presentationData.theme, strings: strongSelf.presentationData.strings, item: .pack(item), menu: menuItems))
} else {
return nil
}

View File

@ -174,7 +174,7 @@ final class HorizontalListContextResultsChatInputContextPanelNode: ChatInputCont
}
})))
}
selectedItemNodeAndContent = (itemNode, StickerPreviewPeekContent(account: item.account, theme: strongSelf.theme, item: .found(FoundStickerItem(file: file, stringRepresentations: [])), menu: menuItems))
selectedItemNodeAndContent = (itemNode, StickerPreviewPeekContent(account: item.account, theme: strongSelf.theme, strings: strongSelf.strings, item: .found(FoundStickerItem(file: file, stringRepresentations: [])), menu: menuItems))
} else {
var menuItems: [ContextMenuItem] = []
if case let .internalReference(internalReference) = item.result, let file = internalReference.file, file.isAnimated {

View File

@ -220,7 +220,7 @@ final class HorizontalStickersChatContextPanelNode: ChatInputContextPanelNode {
}
}))
]
return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.theme, item: .pack(item), menu: menuItems))
return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.theme, strings: strongSelf.strings, item: .pack(item), menu: menuItems))
} else {
return nil
}

View File

@ -175,7 +175,7 @@ private final class InlineReactionSearchStickersNode: ASDisplayNode, UIScrollVie
}
}))
)
return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.theme, item: .pack(item), menu: menuItems))
return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.theme, strings: strongSelf.strings, item: .pack(item), menu: menuItems))
} else {
return nil
}

View File

@ -176,7 +176,7 @@ final class StickersChatInputContextPanelNode: ChatInputContextPanelNode {
}
}))
]
return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.theme, item: .pack(item), menu: menuItems))
return (itemNode, StickerPreviewPeekContent(account: strongSelf.context.account, theme: strongSelf.theme, strings: strongSelf.strings, item: .pack(item), menu: menuItems))
} else {
return nil
}