From 7b95219e148664b418785ee490d7030dd0b31032 Mon Sep 17 00:00:00 2001 From: Ilya Laktyushin Date: Wed, 27 Apr 2022 00:55:00 +0400 Subject: [PATCH] Various Improvements --- .../Telegram-iOS/en.lproj/Localizable.strings | 6 + .../AvatarNode/Sources/AvatarNode.swift | 15 - .../PeekControllerGestureRecognizer.swift | 8 +- submodules/Display/Source/GenerateImage.swift | 15 + .../Display/Source/ImmediateTextNode.swift | 15 +- submodules/Display/Source/TextNode.swift | 22 +- .../Sources/LocationPlaceholderNode.swift | 2 +- .../Sources/MediaPickerPlaceholderNode.swift | 2 +- submodules/PremiumUI/BUILD | 32 ++ .../Sources/PremiumReactionsScreen.swift | 240 +++++++++++ .../Sources/ReactionCarouselNode.swift | 307 ++++++++++++++ .../Sources/ReactionContextNode.swift | 2 +- .../Sources/ReactionSelectionNode.swift | 37 +- .../Sources/SolidRoundedButtonNode.swift | 99 ++--- .../StickerPackPreviewControllerNode.swift | 2 +- .../Sources/StickerPackPreviewGridItem.swift | 61 +-- .../Sources/StickerPackScreen.swift | 31 +- .../Sources/StickerPreviewPeekContent.swift | 16 +- submodules/TabBarUI/Sources/TabBarNode.swift | 4 +- submodules/TelegramApi/Sources/Api0.swift | 9 +- submodules/TelegramApi/Sources/Api1.swift | 386 +++++------------- submodules/TelegramApi/Sources/Api2.swift | 282 +++++++++++++ submodules/TelegramApi/Sources/Api27.swift | 14 +- .../Messages/AttachMenuBots.swift | 86 +++- .../TelegramEngine/Messages/BotWebView.swift | 11 +- submodules/TelegramUI/BUILD | 1 + .../TelegramUI/Images.xcassets/Contents.json | 6 +- .../Premium/ButtonIcon.imageset/Contents.json | 12 + .../ButtonIcon.imageset/premium_24 (2).pdf | 97 +++++ .../Images.xcassets/Premium/Contents.json | 9 + .../Premium/ReactionIcon.imageset/1.pdf | Bin 0 -> 2174 bytes .../ReactionIcon.imageset/Contents.json | 12 + .../StickerIcon.imageset/Contents.json | 12 + .../StickerIcon.imageset/addreactions_32.pdf | 201 +++++++++ .../Sources/AttachmentFileEmptyItem.swift | 15 +- .../Sources/AudioWaveformNode.swift | 23 +- .../TelegramUI/Sources/ChatController.swift | 81 +++- .../Sources/ChatMediaInputNode.swift | 35 +- .../ChatMediaInputStickerGridItem.swift | 57 +-- .../Sources/ChatTextInputPanelNode.swift | 20 +- .../Sources/FeaturedStickersScreen.swift | 4 +- ...textResultsChatInputContextPanelNode.swift | 2 +- ...rizontalStickersChatContextPanelNode.swift | 2 +- .../Sources/InlineReactionSearchPanel.swift | 2 +- .../StickersChatInputContextPanelNode.swift | 2 +- 45 files changed, 1742 insertions(+), 555 deletions(-) create mode 100644 submodules/PremiumUI/BUILD create mode 100644 submodules/PremiumUI/Sources/PremiumReactionsScreen.swift create mode 100644 submodules/PremiumUI/Sources/ReactionCarouselNode.swift create mode 100644 submodules/TelegramUI/Images.xcassets/Premium/ButtonIcon.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Premium/ButtonIcon.imageset/premium_24 (2).pdf create mode 100644 submodules/TelegramUI/Images.xcassets/Premium/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Premium/ReactionIcon.imageset/1.pdf create mode 100644 submodules/TelegramUI/Images.xcassets/Premium/ReactionIcon.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Premium/StickerIcon.imageset/Contents.json create mode 100644 submodules/TelegramUI/Images.xcassets/Premium/StickerIcon.imageset/addreactions_32.pdf diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index 72c0de8514..e0a50eb07b 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -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"; diff --git a/submodules/AvatarNode/Sources/AvatarNode.swift b/submodules/AvatarNode/Sources/AvatarNode.swift index 4d6925bb3a..908cbbcc16 100644 --- a/submodules/AvatarNode/Sources/AvatarNode.swift +++ b/submodules/AvatarNode/Sources/AvatarNode.swift @@ -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 ] diff --git a/submodules/ContextUI/Sources/PeekControllerGestureRecognizer.swift b/submodules/ContextUI/Sources/PeekControllerGestureRecognizer.swift index ab4a8a323f..623cad78ff 100644 --- a/submodules/ContextUI/Sources/PeekControllerGestureRecognizer.swift +++ b/submodules/ContextUI/Sources/PeekControllerGestureRecognizer.swift @@ -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, 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 { diff --git a/submodules/Display/Source/GenerateImage.swift b/submodules/Display/Source/GenerateImage.swift index 19f22159bb..4b05dc557f 100644 --- a/submodules/Display/Source/GenerateImage.swift +++ b/submodules/Display/Source/GenerateImage.swift @@ -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 diff --git a/submodules/Display/Source/ImmediateTextNode.swift b/submodules/Display/Source/ImmediateTextNode.swift index 12799fa47f..a999b324f1 100644 --- a/submodules/Display/Source/ImmediateTextNode.swift +++ b/submodules/Display/Source/ImmediateTextNode.swift @@ -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 } diff --git a/submodules/Display/Source/TextNode.swift b/submodules/Display/Source/TextNode.swift index d7904093ff..abe8e3cc07 100644 --- a/submodules/Display/Source/TextNode.swift +++ b/submodules/Display/Source/TextNode.swift @@ -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) } } diff --git a/submodules/LocationUI/Sources/LocationPlaceholderNode.swift b/submodules/LocationUI/Sources/LocationPlaceholderNode.swift index 94cf0f8fb5..5457f13986 100644 --- a/submodules/LocationUI/Sources/LocationPlaceholderNode.swift +++ b/submodules/LocationUI/Sources/LocationPlaceholderNode.swift @@ -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 { diff --git a/submodules/MediaPickerUI/Sources/MediaPickerPlaceholderNode.swift b/submodules/MediaPickerUI/Sources/MediaPickerPlaceholderNode.swift index fa269a8981..a19535740a 100644 --- a/submodules/MediaPickerUI/Sources/MediaPickerPlaceholderNode.swift +++ b/submodules/MediaPickerUI/Sources/MediaPickerPlaceholderNode.swift @@ -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 { diff --git a/submodules/PremiumUI/BUILD b/submodules/PremiumUI/BUILD new file mode 100644 index 0000000000..6748e54348 --- /dev/null +++ b/submodules/PremiumUI/BUILD @@ -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", + ], +) diff --git a/submodules/PremiumUI/Sources/PremiumReactionsScreen.swift b/submodules/PremiumUI/Sources/PremiumReactionsScreen.swift new file mode 100644 index 0000000000..721cb27c6a --- /dev/null +++ b/submodules/PremiumUI/Sources/PremiumReactionsScreen.swift @@ -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)? + 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)? = 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) + } +} diff --git a/submodules/PremiumUI/Sources/ReactionCarouselNode.swift b/submodules/PremiumUI/Sources/ReactionCarouselNode.swift new file mode 100644 index 0000000000..04179927ba --- /dev/null +++ b/submodules/PremiumUI/Sources/ReactionCarouselNode.swift @@ -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) { + 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 + } + } +} diff --git a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift index b7ec362b8f..0282cc2cfa 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionContextNode.swift @@ -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 diff --git a/submodules/ReactionSelectionNode/Sources/ReactionSelectionNode.swift b/submodules/ReactionSelectionNode/Sources/ReactionSelectionNode.swift index 54a7868642..8abb2e6ffd 100644 --- a/submodules/ReactionSelectionNode/Sources/ReactionSelectionNode.swift +++ b/submodules/ReactionSelectionNode/Sources/ReactionSelectionNode.swift @@ -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) } } diff --git a/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift b/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift index e4f130e6b0..9e832cd22e 100644 --- a/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift +++ b/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift @@ -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 { diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewControllerNode.swift b/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewControllerNode.swift index 11de930dca..711721e617 100644 --- a/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewControllerNode.swift +++ b/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewControllerNode.swift @@ -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 } diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewGridItem.swift b/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewGridItem.swift index c38efca8fc..ae73b7dff5 100644 --- a/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewGridItem.swift +++ b/submodules/StickerPackPreviewUI/Sources/StickerPackPreviewGridItem.swift @@ -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) - } } } diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift b/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift index 18dbbc25a5..e6a730ddd0 100644 --- a/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift +++ b/submodules/StickerPackPreviewUI/Sources/StickerPackScreen.swift @@ -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) { diff --git a/submodules/StickerPackPreviewUI/Sources/StickerPreviewPeekContent.swift b/submodules/StickerPackPreviewUI/Sources/StickerPreviewPeekContent.swift index fd12f2bf3c..aa07b026aa 100644 --- a/submodules/StickerPackPreviewUI/Sources/StickerPreviewPeekContent.swift +++ b/submodules/StickerPackPreviewUI/Sources/StickerPreviewPeekContent.swift @@ -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() diff --git a/submodules/TabBarUI/Sources/TabBarNode.swift b/submodules/TabBarUI/Sources/TabBarNode.swift index ce69593e2e..75750c6c7a 100644 --- a/submodules/TabBarUI/Sources/TabBarNode.swift +++ b/submodules/TabBarUI/Sources/TabBarNode.swift @@ -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() }) }) diff --git a/submodules/TelegramApi/Sources/Api0.swift b/submodules/TelegramApi/Sources/Api0.swift index 896054be6f..8ee1cfe900 100644 --- a/submodules/TelegramApi/Sources/Api0.swift +++ b/submodules/TelegramApi/Sources/Api0.swift @@ -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: diff --git a/submodules/TelegramApi/Sources/Api1.swift b/submodules/TelegramApi/Sources/Api1.swift index e540e8f7ef..39c1ee5559 100644 --- a/submodules/TelegramApi/Sources/Api1.swift +++ b/submodules/TelegramApi/Sources/Api1.swift @@ -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 - } - } - - } -} diff --git a/submodules/TelegramApi/Sources/Api2.swift b/submodules/TelegramApi/Sources/Api2.swift index c28db95811..4d4628afc9 100644 --- a/submodules/TelegramApi/Sources/Api2.swift +++ b/submodules/TelegramApi/Sources/Api2.swift @@ -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) diff --git a/submodules/TelegramApi/Sources/Api27.swift b/submodules/TelegramApi/Sources/Api27.swift index 6bfe54fccb..83c3189443 100644 --- a/submodules/TelegramApi/Sources/Api27.swift +++ b/submodules/TelegramApi/Sources/Api27.swift @@ -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) { + static func prolongWebView(flags: Int32, peer: Api.InputPeer, bot: Api.InputUser, queryId: Int64, replyToMsgId: Int32?, sendAs: Api.InputPeer?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse) { 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) { + 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) { 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() { diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AttachMenuBots.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AttachMenuBots.swift index 17cbfdb114..cd9a7f57c1 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/AttachMenuBots.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/AttachMenuBots.swift @@ -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 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)) } } } diff --git a/submodules/TelegramCore/Sources/TelegramEngine/Messages/BotWebView.swift b/submodules/TelegramCore/Sources/TelegramEngine/Messages/BotWebView.swift index 61603c2e62..74b2e3d28d 100644 --- a/submodules/TelegramCore/Sources/TelegramEngine/Messages/BotWebView.swift +++ b/submodules/TelegramCore/Sources/TelegramEngine/Messages/BotWebView.swift @@ -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 { +private func keepWebViewSignal(network: Network, stateManager: AccountStateManager, flags: Int32, peer: Api.InputPeer, bot: Api.InputUser, queryId: Int64, replyToMessageId: MessageId?, sendAs: Api.InputPeer?) -> Signal { let signal = Signal { subscriber in let poll = Signal { subscriber in - let signal: Signal = network.request(Api.functions.messages.prolongWebView(flags: flags, peer: peer, bot: bot, queryId: queryId, replyToMsgId: replyToMessageId?.id)) + let signal: Signal = 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 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))) } } } diff --git a/submodules/TelegramUI/BUILD b/submodules/TelegramUI/BUILD index 542f1a6e7e..3d1788e6de 100644 --- a/submodules/TelegramUI/BUILD +++ b/submodules/TelegramUI/BUILD @@ -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": [], diff --git a/submodules/TelegramUI/Images.xcassets/Contents.json b/submodules/TelegramUI/Images.xcassets/Contents.json index da4a164c91..73c00596a7 100644 --- a/submodules/TelegramUI/Images.xcassets/Contents.json +++ b/submodules/TelegramUI/Images.xcassets/Contents.json @@ -1,6 +1,6 @@ { "info" : { - "version" : 1, - "author" : "xcode" + "author" : "xcode", + "version" : 1 } -} \ No newline at end of file +} diff --git a/submodules/TelegramUI/Images.xcassets/Premium/ButtonIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Premium/ButtonIcon.imageset/Contents.json new file mode 100644 index 0000000000..c89c9f61c4 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Premium/ButtonIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "premium_24 (2).pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Premium/ButtonIcon.imageset/premium_24 (2).pdf b/submodules/TelegramUI/Images.xcassets/Premium/ButtonIcon.imageset/premium_24 (2).pdf new file mode 100644 index 0000000000..8fe75f92e4 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Premium/ButtonIcon.imageset/premium_24 (2).pdf @@ -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 \ No newline at end of file diff --git a/submodules/TelegramUI/Images.xcassets/Premium/Contents.json b/submodules/TelegramUI/Images.xcassets/Premium/Contents.json new file mode 100644 index 0000000000..6e965652df --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Premium/Contents.json @@ -0,0 +1,9 @@ +{ + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "provides-namespace" : true + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Premium/ReactionIcon.imageset/1.pdf b/submodules/TelegramUI/Images.xcassets/Premium/ReactionIcon.imageset/1.pdf new file mode 100644 index 0000000000000000000000000000000000000000..0d806abecca2911a453b7a275c89e6413e22425b GIT binary patch literal 2174 zcmai0YgiLk8fKM8A;JpCA|m2=i9`i5lN*E*R7e5{Dj{5iA{dehL?MC6fI+Dis4W*o z7ezr_D7Zu}qPwl41w~dCVG)r~kRlXX#O}J>G*T4AvS-4jw14(IbLN}#&NtuX%$s*Y z_T}>J5eFuTd~4+TdlCbJAw|R?l8XyO^+Dy4cof70kRJ(xASz!LjiVT_(IOn>qGE*v zCAqqilsJZpVo0jXF@fG$FIYysL*`-2(>D3dhzAXllYJ~kdD-ummThZ3&e_?vbH@dH zLBl>l)u!iTLG||s8h_6DWpD7AUnb+GCp=t_Pv^htoqFBymi44|n38azedXhe{e&@UYQ`To0UEKZU2Mc_z+s6#_C0eSo z61X?g27Oo8wQOcr8gT7F!I7_<-vwZ|c38^Nt3)17$}8d`ozaR|3`IkbuYCGcBEFT3I0%8#iJ0OLr)5YOa3zq`Y+W zo}hyheXIWY)JyXLwP)b=>{@=&scS{KLuv8d5fTQEzI$AqV{1IzQ)c#K$G?)f722e8 z!}QLY`1?+b-0#gY;1Ko&zS3nh+oq-K{@EQ?Q^A6c+`pIZ6S|3?{&m|#zvPjgIAztp zy+p+c6UMhfCPD%XTm3I|Gt^aMRz8@jyeP;>P*~`#_kH2}QFGaqXSJNoVF{v8`>xC* z`xKH(ozohl3gtti*u+OAF~*zFyKP^tAE^w;{l_7#Ouff2?}}O4+eal^+-yT~D)Mvx z^i|6K&InKaogM;%OD0>QqAOQKXwR;!K}-cn(ZX&i5*qY{P?#u>pnc_WDyyc>Ddu2> z-krR1G ztEZpZLutB`(r>4+LPt!Ru6Z<@Mo2w5(yTXi3D=uej0Kn=f9P}V+LC6i&DZOoSEo#l z4A#PH6UeF;2d9%Ame+sc)6h(bTkFtw*l7KxHHM$_J|UN6?rD-znc9+9m$Flf^Qn_9 zZZ_R7t*q;;Gx%0bobc6u8OWW%|C#H2VuzizUUsPdR3uq5WZ}Macb&{o64Yy2 z{UOl6c-O%2+O`NA=Vdl^=ke0vBMn_8(gUwN5?$|ZA}1Fi_QA|#5iK%@|a#wHwyC4E86bn?Yh;T}R9NEhVd zLI7Y;MKD-ERQFvF0#U^xa0Y?~Zh$j5=Hk#6;y_gY#8?!f`ide^hzudRxV|C`mE+*i zd7a#YN@OB8g$nu@Krlq75yt_Yue(By17{^f2<@z@dSHt9*hMAHX3(MYRSH2~Oe9yv z5}Cw_^RO3$WAW(x${l!d(FBzSm~g^BDZeUC2|KJ4_tvkE_@!J55o*L46}G(y1}p`9av%_;2H*&3BxpC>0nI* zJ_{Cr1Wt1TK43aB<|e?181!A>9*9j$*8hu7Y66_qnu;RuRK#>sFValP9#wG z+<;h2CY=dMp*aj;0xIe5LGlHRj(`_Ij71oY0T}jM42BW#L-{R+&}e`Kzr&bpQ2p;P zCnrGVc?`!yvS<|3{Ywzaj-sGsf-`;!1r8C7&l1~9E>%DTMRc4~;vx*!b%?O(G!mK2 I+s7yU5A*Xh&j0`b literal 0 HcmV?d00001 diff --git a/submodules/TelegramUI/Images.xcassets/Premium/ReactionIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Premium/ReactionIcon.imageset/Contents.json new file mode 100644 index 0000000000..f32dceb906 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Premium/ReactionIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "1.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Premium/StickerIcon.imageset/Contents.json b/submodules/TelegramUI/Images.xcassets/Premium/StickerIcon.imageset/Contents.json new file mode 100644 index 0000000000..2acd1b3c24 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Premium/StickerIcon.imageset/Contents.json @@ -0,0 +1,12 @@ +{ + "images" : [ + { + "filename" : "addreactions_32.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + } +} diff --git a/submodules/TelegramUI/Images.xcassets/Premium/StickerIcon.imageset/addreactions_32.pdf b/submodules/TelegramUI/Images.xcassets/Premium/StickerIcon.imageset/addreactions_32.pdf new file mode 100644 index 0000000000..f469aa5214 --- /dev/null +++ b/submodules/TelegramUI/Images.xcassets/Premium/StickerIcon.imageset/addreactions_32.pdf @@ -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 \ No newline at end of file diff --git a/submodules/TelegramUI/Sources/AttachmentFileEmptyItem.swift b/submodules/TelegramUI/Sources/AttachmentFileEmptyItem.swift index 37e4f8dcbd..9a0bbe3d16 100644 --- a/submodules/TelegramUI/Sources/AttachmentFileEmptyItem.swift +++ b/submodules/TelegramUI/Sources/AttachmentFileEmptyItem.swift @@ -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))) diff --git a/submodules/TelegramUI/Sources/AudioWaveformNode.swift b/submodules/TelegramUI/Sources/AudioWaveformNode.swift index a42ec30ee8..74badd28df 100644 --- a/submodules/TelegramUI/Sources/AudioWaveformNode.swift +++ b/submodules/TelegramUI/Sources/AudioWaveformNode.swift @@ -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] diff --git a/submodules/TelegramUI/Sources/ChatController.swift b/submodules/TelegramUI/Sources/ChatController.swift index 88cdc3fe6f..0ae99c762a 100644 --- a/submodules/TelegramUI/Sources/ChatController.swift +++ b/submodules/TelegramUI/Sources/ChatController.swift @@ -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) diff --git a/submodules/TelegramUI/Sources/ChatMediaInputNode.swift b/submodules/TelegramUI/Sources/ChatMediaInputNode.swift index e2cf7fe265..8c0c3014ba 100644 --- a/submodules/TelegramUI/Sources/ChatMediaInputNode.swift +++ b/submodules/TelegramUI/Sources/ChatMediaInputNode.swift @@ -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 diff --git a/submodules/TelegramUI/Sources/ChatMediaInputStickerGridItem.swift b/submodules/TelegramUI/Sources/ChatMediaInputStickerGridItem.swift index 2ce25c5f5b..81f5c9d977 100644 --- a/submodules/TelegramUI/Sources/ChatMediaInputStickerGridItem.swift +++ b/submodules/TelegramUI/Sources/ChatMediaInputStickerGridItem.swift @@ -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) + } } } diff --git a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift index 430b988f26..3c128a0f38 100644 --- a/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift +++ b/submodules/TelegramUI/Sources/ChatTextInputPanelNode.swift @@ -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) diff --git a/submodules/TelegramUI/Sources/FeaturedStickersScreen.swift b/submodules/TelegramUI/Sources/FeaturedStickersScreen.swift index d83db29608..0c9160ae16 100644 --- a/submodules/TelegramUI/Sources/FeaturedStickersScreen.swift +++ b/submodules/TelegramUI/Sources/FeaturedStickersScreen.swift @@ -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 } diff --git a/submodules/TelegramUI/Sources/HorizontalListContextResultsChatInputContextPanelNode.swift b/submodules/TelegramUI/Sources/HorizontalListContextResultsChatInputContextPanelNode.swift index 0bc5bb2c19..4f9b362ab9 100644 --- a/submodules/TelegramUI/Sources/HorizontalListContextResultsChatInputContextPanelNode.swift +++ b/submodules/TelegramUI/Sources/HorizontalListContextResultsChatInputContextPanelNode.swift @@ -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 { diff --git a/submodules/TelegramUI/Sources/HorizontalStickersChatContextPanelNode.swift b/submodules/TelegramUI/Sources/HorizontalStickersChatContextPanelNode.swift index 4691ba0276..6a15cb7543 100755 --- a/submodules/TelegramUI/Sources/HorizontalStickersChatContextPanelNode.swift +++ b/submodules/TelegramUI/Sources/HorizontalStickersChatContextPanelNode.swift @@ -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 } diff --git a/submodules/TelegramUI/Sources/InlineReactionSearchPanel.swift b/submodules/TelegramUI/Sources/InlineReactionSearchPanel.swift index 9827474fbc..ba1d5a9529 100644 --- a/submodules/TelegramUI/Sources/InlineReactionSearchPanel.swift +++ b/submodules/TelegramUI/Sources/InlineReactionSearchPanel.swift @@ -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 } diff --git a/submodules/TelegramUI/Sources/StickersChatInputContextPanelNode.swift b/submodules/TelegramUI/Sources/StickersChatInputContextPanelNode.swift index b0c001a81d..0a675f3210 100644 --- a/submodules/TelegramUI/Sources/StickersChatInputContextPanelNode.swift +++ b/submodules/TelegramUI/Sources/StickersChatInputContextPanelNode.swift @@ -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 }