diff --git a/Telegram/Telegram-iOS/Resources/IntroLetter.tgs b/Telegram/Telegram-iOS/Resources/IntroLetter.tgs index 4964d09680..a5c3978790 100644 Binary files a/Telegram/Telegram-iOS/Resources/IntroLetter.tgs and b/Telegram/Telegram-iOS/Resources/IntroLetter.tgs differ diff --git a/Telegram/Telegram-iOS/en.lproj/Localizable.strings b/Telegram/Telegram-iOS/en.lproj/Localizable.strings index e6fcf95672..396cae1d26 100644 --- a/Telegram/Telegram-iOS/en.lproj/Localizable.strings +++ b/Telegram/Telegram-iOS/en.lproj/Localizable.strings @@ -7973,5 +7973,7 @@ Sorry for the inconvenience."; "Settings.ChangeProfilePhoto" = "Change Profile Photo"; -"Premium.EmojiStatusTitle" = "This is %1$@'s current status from %2$@."; +"Premium.EmojiStatusTitle" = "This is %1$@'s current status from #[%2$@]()."; "Premium.EmojiStatusText" = "Emoji status is a premium feature.\n Other features included in **Telegram Premium**:"; + +"Login.SelectCountry" = "Country"; diff --git a/submodules/AccountContext/Sources/AccountContext.swift b/submodules/AccountContext/Sources/AccountContext.swift index 58096db6ec..9821fd95de 100644 --- a/submodules/AccountContext/Sources/AccountContext.swift +++ b/submodules/AccountContext/Sources/AccountContext.swift @@ -780,7 +780,7 @@ public enum PremiumIntroSource { case about case deeplink(String?) case profile(PeerId) - case emojiStatus(PeerId, Int64) + case emojiStatus(PeerId, Int64, TelegramMediaFile?, String?) } #if ENABLE_WALLET diff --git a/submodules/AuthorizationUI/Sources/AuthorizationOptionText.swift b/submodules/AuthorizationUI/Sources/AuthorizationOptionText.swift index 8fd5bf5900..c55e626486 100644 --- a/submodules/AuthorizationUI/Sources/AuthorizationOptionText.swift +++ b/submodules/AuthorizationUI/Sources/AuthorizationOptionText.swift @@ -7,26 +7,32 @@ import TextFormat import Markdown public func authorizationCurrentOptionText(_ type: SentAuthorizationCodeType, strings: PresentationStrings, primaryColor: UIColor, accentColor: UIColor) -> NSAttributedString { + let fontSize: CGFloat = 17.0 switch type { case .sms: - return NSAttributedString(string: strings.Login_CodeSentSms, font: Font.regular(16.0), textColor: primaryColor, paragraphAlignment: .center) + return NSAttributedString(string: strings.Login_CodeSentSms, font: Font.regular(fontSize), textColor: primaryColor, paragraphAlignment: .center) case .otherSession: - let body = MarkdownAttributeSet(font: Font.regular(16.0), textColor: primaryColor) - let bold = MarkdownAttributeSet(font: Font.semibold(16.0), textColor: primaryColor) + let body = MarkdownAttributeSet(font: Font.regular(fontSize), textColor: primaryColor) + let bold = MarkdownAttributeSet(font: Font.semibold(fontSize), textColor: primaryColor) return parseMarkdownIntoAttributedString(strings.Login_CodeSentInternal, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in nil }), textAlignment: .center) case .missedCall: - let body = MarkdownAttributeSet(font: Font.regular(16.0), textColor: primaryColor) - let bold = MarkdownAttributeSet(font: Font.semibold(16.0), textColor: primaryColor) + let body = MarkdownAttributeSet(font: Font.regular(fontSize), textColor: primaryColor) + let bold = MarkdownAttributeSet(font: Font.semibold(fontSize), textColor: primaryColor) return parseMarkdownIntoAttributedString(strings.Login_ShortCallTitle, attributes: MarkdownAttributes(body: body, bold: bold, link: body, linkAttribute: { _ in nil }), textAlignment: .center) case .call: - return NSAttributedString(string: strings.Login_CodeSentCall, font: Font.regular(16.0), textColor: primaryColor, paragraphAlignment: .center) + return NSAttributedString(string: strings.Login_CodeSentCall, font: Font.regular(fontSize), textColor: primaryColor, paragraphAlignment: .center) case .flashCall: - return NSAttributedString(string: strings.ChangePhoneNumberCode_Called, font: Font.regular(16.0), textColor: primaryColor, paragraphAlignment: .center) + return NSAttributedString(string: strings.ChangePhoneNumberCode_Called, font: Font.regular(fontSize), textColor: primaryColor, paragraphAlignment: .center) case .emailSetupRequired: - return NSAttributedString(string: "", font: Font.regular(16.0), textColor: primaryColor, paragraphAlignment: .center) + return NSAttributedString(string: "", font: Font.regular(fontSize), textColor: primaryColor, paragraphAlignment: .center) case let .email(emailPattern, _, _, _, _): //TODO: localize - return NSAttributedString(string: "Please enter the code we have sent to your email \(emailPattern).", font: Font.regular(16.0), textColor: primaryColor, paragraphAlignment: .center) + let mutableString = NSAttributedString(string: "Please enter the code we have sent to your email \(emailPattern).", font: Font.regular(fontSize), textColor: primaryColor, paragraphAlignment: .center).mutableCopy() as! NSMutableAttributedString + let range = (mutableString.string as NSString).range(of: "*******") + if range.location != NSNotFound { + mutableString.addAttribute(NSAttributedString.Key(rawValue: TelegramTextAttributes.Spoiler), value: true, range: range) + } + return mutableString } } diff --git a/submodules/ChatListUI/Sources/Node/ChatListItem.swift b/submodules/ChatListUI/Sources/Node/ChatListItem.swift index 1287d19cc7..a6501bdf70 100644 --- a/submodules/ChatListUI/Sources/Node/ChatListItem.swift +++ b/submodules/ChatListUI/Sources/Node/ChatListItem.swift @@ -1524,7 +1524,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { if let peer = messages.last?.author { if case let .user(user) = peer, let emojiStatus = user.emojiStatus { currentCredibilityIconImage = PresentationResourcesChatList.premiumIcon(item.presentationData.theme) - currentCredibilityIconContent = .emojiStatus(status: emojiStatus, placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor) + currentCredibilityIconContent = .emojiStatus(status: emojiStatus, size: CGSize(width: 32.0, height: 32.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor) } else if peer.isScam { currentCredibilityIconImage = PresentationResourcesChatList.scamIcon(item.presentationData.theme, strings: item.presentationData.strings, type: .regular) currentCredibilityIconContent = .scam(color: item.presentationData.theme.chat.message.incoming.scamColor) @@ -1545,7 +1545,7 @@ class ChatListItemNode: ItemListRevealOptionsItemNode { } else if case let .chat(itemPeer) = contentPeer, let peer = itemPeer.chatMainPeer { if case let .user(user) = peer, let emojiStatus = user.emojiStatus { currentCredibilityIconImage = PresentationResourcesChatList.premiumIcon(item.presentationData.theme) - currentCredibilityIconContent = .emojiStatus(status: emojiStatus, placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor) + currentCredibilityIconContent = .emojiStatus(status: emojiStatus, size: CGSize(width: 32.0, height: 32.0), placeholderColor: item.presentationData.theme.list.mediaPlaceholderColor) } else if peer.isScam { currentCredibilityIconImage = PresentationResourcesChatList.scamIcon(item.presentationData.theme, strings: item.presentationData.strings, type: .regular) currentCredibilityIconContent = .scam(color: item.presentationData.theme.chat.message.incoming.scamColor) diff --git a/submodules/CodeInputView/Sources/CodeInputView.swift b/submodules/CodeInputView/Sources/CodeInputView.swift index ba82a935af..50c4795935 100644 --- a/submodules/CodeInputView/Sources/CodeInputView.swift +++ b/submodules/CodeInputView/Sources/CodeInputView.swift @@ -48,28 +48,38 @@ public final class CodeInputView: ASDisplayNode, UITextFieldDelegate { fatalError("init(coder:) has not been implemented") } + override func didLoad() { + super.didLoad() + + self.textNode.layer.anchorPoint = CGPoint(x: 0.5, y: 1.0) + } + func update(borderColor: UInt32, isHighlighted: Bool) { if self.borderColorValue != borderColor { self.borderColorValue = borderColor let previousColor = self.backgroundView.layer.borderColor - self.backgroundView.layer.cornerRadius = 5.0 + self.backgroundView.layer.cornerRadius = 15.0 + if #available(iOS 13.0, *) { + self.backgroundView.layer.cornerCurve = .continuous + } self.backgroundView.layer.borderColor = UIColor(argb: borderColor).cgColor - self.backgroundView.layer.borderWidth = 1.0 + self.backgroundView.layer.borderWidth = 1.0 + UIScreenPixel if let previousColor = previousColor { self.backgroundView.layer.animate(from: previousColor, to: UIColor(argb: borderColor).cgColor, keyPath: "borderColor", timingFunction: CAMediaTimingFunctionName.linear.rawValue, duration: 0.15) } } } - func update(textColor: UInt32, text: String, size: CGSize, animated: Bool) { + func update(textColor: UInt32, text: String, size: CGSize, fontSize: CGFloat, animated: Bool) { let previousText = self.text self.text = text if animated && previousText.isEmpty != text.isEmpty { if !text.isEmpty { - self.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1) - self.textNode.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: 0.0, y: size.height / 2.0)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: 0.5, additive: true) + self.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.25) + self.textNode.layer.animateSpring(from: NSValue(cgPoint: CGPoint(x: 0.0, y: size.height * 0.35)), to: NSValue(cgPoint: CGPoint()), keyPath: "position", duration: 0.4, damping: 70.0, additive: true) + self.textNode.layer.animateScaleY(from: 0.01, to: 1.0, duration: 0.25) } else { if let copyView = self.textNode.view.snapshotContentTree() { self.view.insertSubview(copyView, at: 0) @@ -81,8 +91,6 @@ public final class CodeInputView: ASDisplayNode, UITextFieldDelegate { } } - let fontSize: CGFloat = floor(21.0 * size.height / 28.0) - if #available(iOS 13.0, *) { self.textNode.attributedText = NSAttributedString(string: text, font: UIFont.monospacedSystemFont(ofSize: fontSize, weight: .regular), textColor: UIColor(argb: textColor)) } else { @@ -105,6 +113,7 @@ public final class CodeInputView: ASDisplayNode, UITextFieldDelegate { private var theme: Theme? private var count: Int? + private var prefix: String = "" private var textValue: String = "" public var text: String { @@ -215,6 +224,15 @@ public final class CodeInputView: ASDisplayNode, UITextFieldDelegate { let itemView = self.itemViews[i] let itemSize = itemView.bounds.size + let fontSize: CGFloat + if self.prefix.isEmpty { + let height: CGFloat = 51.0 + fontSize = floor(13.0 * height / 28.0) + } else { + let height: CGFloat = 28.0 + fontSize = floor(21.0 * height / 28.0) + } + itemView.update(borderColor: self.focusIndex == i ? theme.activeBorder : theme.inactiveBorder, isHighlighted: self.focusIndex == i) let itemText: String if i < self.textValue.count { @@ -222,13 +240,14 @@ public final class CodeInputView: ASDisplayNode, UITextFieldDelegate { } else { itemText = "" } - itemView.update(textColor: theme.foreground, text: itemText, size: itemSize, animated: animated) + itemView.update(textColor: theme.foreground, text: itemText, size: itemSize, fontSize: fontSize, animated: animated) } } public func update(theme: Theme, prefix: String, count: Int, width: CGFloat) -> CGSize { self.theme = theme self.count = count + self.prefix = prefix if theme.isDark { self.textField.keyboardAppearance = .dark @@ -236,11 +255,14 @@ public final class CodeInputView: ASDisplayNode, UITextFieldDelegate { self.textField.keyboardAppearance = .light } + let fontSize: CGFloat let height: CGFloat if prefix.isEmpty { - height = 40.0 + height = 51.0 + fontSize = floor(13.0 * height / 28.0) } else { height = 28.0 + fontSize = floor(21.0 * height / 28.0) } if #available(iOS 13.0, *) { @@ -251,8 +273,8 @@ public final class CodeInputView: ASDisplayNode, UITextFieldDelegate { let prefixSize = self.prefixLabel.updateLayout(CGSize(width: width, height: 100.0)) let prefixSpacing: CGFloat = prefix.isEmpty ? 0.0 : 8.0 - let itemSize = CGSize(width: floor(25.0 * height / 28.0), height: height) - let itemSpacing: CGFloat = 5.0 + let itemSize = CGSize(width: floor(24.0 * height / 28.0), height: height) + let itemSpacing: CGFloat = prefix.isEmpty ? 15.0 : 5.0 let itemsWidth: CGFloat = itemSize.width * CGFloat(count) + itemSpacing * CGFloat(count - 1) let contentWidth: CGFloat = prefixSize.width + prefixSpacing + itemsWidth @@ -276,7 +298,7 @@ public final class CodeInputView: ASDisplayNode, UITextFieldDelegate { } else { itemText = "" } - itemView.update(textColor: theme.foreground, text: itemText, size: itemSize, animated: false) + itemView.update(textColor: theme.foreground, text: itemText, size: itemSize, fontSize: fontSize, animated: false) itemView.frame = CGRect(origin: CGPoint(x: contentOriginX + prefixSize.width + prefixSpacing + CGFloat(i) * (itemSize.width + itemSpacing), y: 0.0), size: itemSize) } if self.itemViews.count > count { diff --git a/submodules/Components/MultilineTextWithEntitiesComponent/BUILD b/submodules/Components/MultilineTextWithEntitiesComponent/BUILD new file mode 100644 index 0000000000..f6050aa2dc --- /dev/null +++ b/submodules/Components/MultilineTextWithEntitiesComponent/BUILD @@ -0,0 +1,26 @@ +load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library") + +swift_library( + name = "MultilineTextWithEntitiesComponent", + module_name = "MultilineTextWithEntitiesComponent", + srcs = glob([ + "Sources/**/*.swift", + ]), + copts = [ + "-warnings-as-errors", + ], + deps = [ + "//submodules/Display:Display", + "//submodules/ComponentFlow:ComponentFlow", + "//submodules/Markdown:Markdown", + "//submodules/TextFormat:TextFormat", + "//submodules/AccountContext:AccountContext", + "//submodules/TelegramUI/Components/EmojiTextAttachmentView:EmojiTextAttachmentView", + "//submodules/TelegramUI/Components/AnimationCache:AnimationCache", + "//submodules/TelegramUI/Components/MultiAnimationRenderer:MultiAnimationRenderer", + "//submodules/TelegramUI/Components/TextNodeWithEntities:TextNodeWithEntities", + ], + visibility = [ + "//visibility:public", + ], +) diff --git a/submodules/Components/MultilineTextWithEntitiesComponent/Sources/MultilineTextWithEntitiesComponent.swift b/submodules/Components/MultilineTextWithEntitiesComponent/Sources/MultilineTextWithEntitiesComponent.swift new file mode 100644 index 0000000000..c48a0bf5e5 --- /dev/null +++ b/submodules/Components/MultilineTextWithEntitiesComponent/Sources/MultilineTextWithEntitiesComponent.swift @@ -0,0 +1,211 @@ +import Foundation +import UIKit +import ComponentFlow +import Display +import Markdown +import TextNodeWithEntities +import AccountContext +import AnimationCache +import MultiAnimationRenderer + +public final class MultilineTextWithEntitiesComponent: Component { + public enum TextContent: Equatable { + case plain(NSAttributedString) + case markdown(text: String, attributes: MarkdownAttributes) + } + + public let context: AccountContext? + public let animationCache: AnimationCache? + public let animationRenderer: MultiAnimationRenderer? + public let placeholderColor: UIColor? + + public let text: TextContent + public let horizontalAlignment: NSTextAlignment + public let verticalAlignment: TextVerticalAlignment + public let truncationType: CTLineTruncationType + public let maximumNumberOfLines: Int + public let lineSpacing: CGFloat + public let cutout: TextNodeCutout? + public let insets: UIEdgeInsets + public let textShadowColor: UIColor? + public let textStroke: (UIColor, CGFloat)? + public let highlightColor: UIColor? + public let highlightAction: (([NSAttributedString.Key: Any]) -> NSAttributedString.Key?)? + public let tapAction: (([NSAttributedString.Key: Any], Int) -> Void)? + public let longTapAction: (([NSAttributedString.Key: Any], Int) -> Void)? + + public init( + context: AccountContext?, + animationCache: AnimationCache?, + animationRenderer: MultiAnimationRenderer?, + placeholderColor: UIColor?, + text: TextContent, + horizontalAlignment: NSTextAlignment = .natural, + verticalAlignment: TextVerticalAlignment = .top, + truncationType: CTLineTruncationType = .end, + maximumNumberOfLines: Int = 1, + lineSpacing: CGFloat = 0.0, + cutout: TextNodeCutout? = nil, + insets: UIEdgeInsets = UIEdgeInsets(), + textShadowColor: UIColor? = nil, + textStroke: (UIColor, CGFloat)? = nil, + highlightColor: UIColor? = nil, + highlightAction: (([NSAttributedString.Key: Any]) -> NSAttributedString.Key?)? = nil, + tapAction: (([NSAttributedString.Key: Any], Int) -> Void)? = nil, + longTapAction: (([NSAttributedString.Key: Any], Int) -> Void)? = nil + ) { + self.context = context + self.animationCache = animationCache + self.animationRenderer = animationRenderer + self.placeholderColor = placeholderColor + self.text = text + self.horizontalAlignment = horizontalAlignment + self.verticalAlignment = verticalAlignment + self.truncationType = truncationType + self.maximumNumberOfLines = maximumNumberOfLines + self.lineSpacing = lineSpacing + self.cutout = cutout + self.insets = insets + self.textShadowColor = textShadowColor + self.textStroke = textStroke + self.highlightColor = highlightColor + self.highlightAction = highlightAction + self.tapAction = tapAction + self.longTapAction = longTapAction + } + + public static func ==(lhs: MultilineTextWithEntitiesComponent, rhs: MultilineTextWithEntitiesComponent) -> Bool { + if lhs.text != rhs.text { + return false + } + if lhs.horizontalAlignment != rhs.horizontalAlignment { + return false + } + if lhs.verticalAlignment != rhs.verticalAlignment { + return false + } + if lhs.truncationType != rhs.truncationType { + return false + } + if lhs.maximumNumberOfLines != rhs.maximumNumberOfLines { + return false + } + if lhs.lineSpacing != rhs.lineSpacing { + return false + } + if lhs.cutout != rhs.cutout { + return false + } + if lhs.insets != rhs.insets { + return false + } + + if let lhsTextShadowColor = lhs.textShadowColor, let rhsTextShadowColor = rhs.textShadowColor { + if !lhsTextShadowColor.isEqual(rhsTextShadowColor) { + return false + } + } else if (lhs.textShadowColor != nil) != (rhs.textShadowColor != nil) { + return false + } + + if let lhsTextStroke = lhs.textStroke, let rhsTextStroke = rhs.textStroke { + if !lhsTextStroke.0.isEqual(rhsTextStroke.0) { + return false + } + if lhsTextStroke.1 != rhsTextStroke.1 { + return false + } + } else if (lhs.textShadowColor != nil) != (rhs.textShadowColor != nil) { + return false + } + + if let lhsHighlightColor = lhs.highlightColor, let rhsHighlightColor = rhs.highlightColor { + if !lhsHighlightColor.isEqual(rhsHighlightColor) { + return false + } + } else if (lhs.highlightColor != nil) != (rhs.highlightColor != nil) { + return false + } + + return true + } + + public final class View: UIView { + let textNode: ImmediateTextNodeWithEntities + + public override init(frame: CGRect) { + self.textNode = ImmediateTextNodeWithEntities() + + super.init(frame: frame) + + self.addSubview(self.textNode.view) + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + public func update(component: MultilineTextWithEntitiesComponent, availableSize: CGSize, transition: Transition) -> CGSize { + let attributedString: NSAttributedString + switch component.text { + case let .plain(string): + attributedString = string + case let .markdown(text, attributes): + attributedString = parseMarkdownIntoAttributedString(text, attributes: attributes) + } + + let previousText = self.textNode.attributedText?.string + + self.textNode.attributedText = attributedString + self.textNode.maximumNumberOfLines = component.maximumNumberOfLines + self.textNode.truncationType = component.truncationType + self.textNode.textAlignment = component.horizontalAlignment + self.textNode.verticalAlignment = component.verticalAlignment + self.textNode.lineSpacing = component.lineSpacing + self.textNode.cutout = component.cutout + self.textNode.insets = component.insets + self.textNode.textShadowColor = component.textShadowColor + self.textNode.textStroke = component.textStroke + self.textNode.linkHighlightColor = component.highlightColor + self.textNode.highlightAttributeAction = component.highlightAction + self.textNode.tapAttributeAction = component.tapAction + self.textNode.longTapAttributeAction = component.longTapAction + + if case let .curve(duration, _) = transition.animation, let previousText = previousText, previousText != attributedString.string { + if let snapshotView = self.snapshotView(afterScreenUpdates: false) { + snapshotView.center = self.center + self.superview?.addSubview(snapshotView) + + snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: duration, removeOnCompletion: false, completion: { [weak snapshotView] _ in + snapshotView?.removeFromSuperview() + }) + self.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration) + } + } + + let size = self.textNode.updateLayout(availableSize) + self.textNode.frame = CGRect(origin: .zero, size: size) + + self.textNode.visibility = true + if let context = component.context, let animationCache = component.animationCache, let animationRenderer = component.animationRenderer, let placeholderColor = component.placeholderColor { + self.textNode.arguments = TextNodeWithEntities.Arguments( + context: context, + cache: animationCache, + renderer: animationRenderer, + placeholderColor: placeholderColor, + attemptSynchronous: false + ) + } + + return size + } + } + + public func makeView() -> View { + return View() + } + + public func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, transition: transition) + } +} diff --git a/submodules/Display/Source/ContainerViewLayout.swift b/submodules/Display/Source/ContainerViewLayout.swift index 6c1fd8d557..edf00a8555 100644 --- a/submodules/Display/Source/ContainerViewLayout.swift +++ b/submodules/Display/Source/ContainerViewLayout.swift @@ -155,6 +155,10 @@ public extension ContainerViewLayout { return self.size.width > self.size.height ? .landscape : .portrait } + var standardKeyboardHeight: CGFloat { + return self.deviceMetrics.keyboardHeight(inLandscape: self.orientation == .landscape) + } + var standardInputHeight: CGFloat { return self.deviceMetrics.keyboardHeight(inLandscape: self.orientation == .landscape) + self.deviceMetrics.predictiveInputHeight(inLandscape: self.orientation == .landscape) } diff --git a/submodules/Display/Source/Font.swift b/submodules/Display/Source/Font.swift index e7de5243eb..b3fa406300 100644 --- a/submodules/Display/Source/Font.swift +++ b/submodules/Display/Source/Font.swift @@ -165,19 +165,11 @@ public struct Font { } public static func medium(_ size: CGFloat) -> UIFont { - if #available(iOS 8.2, *) { - return UIFont.systemFont(ofSize: size, weight: UIFont.Weight.medium) - } else { - return CTFontCreateWithName("HelveticaNeue-Medium" as CFString, size, nil) - } + return UIFont.systemFont(ofSize: size, weight: UIFont.Weight.medium) } public static func semibold(_ size: CGFloat) -> UIFont { - if #available(iOS 8.2, *) { - return UIFont.systemFont(ofSize: size, weight: UIFont.Weight.semibold) - } else { - return CTFontCreateWithName("HelveticaNeue-Medium" as CFString, size, nil) - } + return UIFont.systemFont(ofSize: size, weight: UIFont.Weight.semibold) } public static func bold(_ size: CGFloat) -> UIFont { @@ -188,17 +180,12 @@ public struct Font { } } - public static func heavy(_ size: CGFloat) -> UIFont { return self.with(size: size, design: .regular, weight: .heavy, traits: []) } public static func light(_ size: CGFloat) -> UIFont { - if #available(iOS 8.2, *) { - return UIFont.systemFont(ofSize: size, weight: UIFont.Weight.light) - } else { - return CTFontCreateWithName("HelveticaNeue-Light" as CFString, size, nil) - } + return UIFont.systemFont(ofSize: size, weight: UIFont.Weight.light) } public static func semiboldItalic(_ size: CGFloat) -> UIFont { diff --git a/submodules/PasscodeUI/Sources/PasscodeEntryKeyboardNode.swift b/submodules/PasscodeUI/Sources/PasscodeEntryKeyboardNode.swift index 7cd90fae47..649b98978d 100644 --- a/submodules/PasscodeUI/Sources/PasscodeEntryKeyboardNode.swift +++ b/submodules/PasscodeUI/Sources/PasscodeEntryKeyboardNode.swift @@ -8,20 +8,12 @@ import GradientBackground private let regularTitleFont = Font.regular(36.0) private let regularSubtitleFont: UIFont = { - if #available(iOS 8.2, *) { - return UIFont.systemFont(ofSize: 10.0, weight: UIFont.Weight.bold) - } else { - return CTFontCreateWithName("HelveticaNeue-Bold" as CFString, 10.0, nil) - } + return UIFont.systemFont(ofSize: 10.0, weight: UIFont.Weight.bold) }() private let largeTitleFont = Font.regular(40.0) private let largeSubtitleFont: UIFont = { - if #available(iOS 8.2, *) { - return UIFont.systemFont(ofSize: 12.0, weight: UIFont.Weight.bold) - } else { - return CTFontCreateWithName("HelveticaNeue-Bold" as CFString, 12.0, nil) - } + return UIFont.systemFont(ofSize: 12.0, weight: UIFont.Weight.bold) }() private func generateButtonImage(background: PasscodeBackground, frame: CGRect, title: String, subtitle: String, highlighted: Bool) -> UIImage? { diff --git a/submodules/PhoneInputNode/Sources/PhoneInputNode.swift b/submodules/PhoneInputNode/Sources/PhoneInputNode.swift index ed3a8fd7ba..e82fab23c5 100644 --- a/submodules/PhoneInputNode/Sources/PhoneInputNode.swift +++ b/submodules/PhoneInputNode/Sources/PhoneInputNode.swift @@ -177,7 +177,7 @@ public final class PhoneInputNode: ASDisplayNode, UITextFieldDelegate { private func updatePlaceholder() { if let mask = self.mask { let mutableMask = NSMutableAttributedString(attributedString: mask) - mutableMask.replaceCharacters(in: NSRange(location: 0, length: mask.string.count), with: mask.string.replacingOccurrences(of: "X", with: "–")) + mutableMask.replaceCharacters(in: NSRange(location: 0, length: mask.string.count), with: mask.string.replacingOccurrences(of: "X", with: "0")) if let text = self.numberField.textField.text { mutableMask.replaceCharacters(in: NSRange(location: 0, length: min(text.count, mask.string.count)), with: text) } diff --git a/submodules/PremiumUI/BUILD b/submodules/PremiumUI/BUILD index cbb0e38f16..171031865a 100644 --- a/submodules/PremiumUI/BUILD +++ b/submodules/PremiumUI/BUILD @@ -76,12 +76,6 @@ swift_library( "//submodules/SolidRoundedButtonNode:SolidRoundedButtonNode", "//submodules/PresentationDataUtils:PresentationDataUtils", "//submodules/ReactionSelectionNode:ReactionSelectionNode", - "//submodules/ComponentFlow:ComponentFlow", - "//submodules/Components/ViewControllerComponent:ViewControllerComponent", - "//submodules/Components/MultilineTextComponent:MultilineTextComponent", - "//submodules/Components/SheetComponent:SheetComponent", - "//submodules/Components/BundleIconComponent:BundleIconComponent", - "//submodules/Components/SolidRoundedButtonComponent:SolidRoundedButtonComponent", "//submodules/InAppPurchaseManager:InAppPurchaseManager", "//submodules/ConfettiEffect:ConfettiEffect", "//submodules/TextFormat:TextFormat", @@ -93,6 +87,14 @@ swift_library( "//submodules/ShimmerEffect:ShimmerEffect", "//submodules/LegacyComponents:LegacyComponents", "//submodules/CheckNode:CheckNode", + "//submodules/ComponentFlow:ComponentFlow", + "//submodules/Components/ViewControllerComponent:ViewControllerComponent", + "//submodules/Components/MultilineTextComponent:MultilineTextComponent", + "//submodules/Components/MultilineTextWithEntitiesComponent:MultilineTextWithEntitiesComponent", + "//submodules/Components/SheetComponent:SheetComponent", + "//submodules/Components/BundleIconComponent:BundleIconComponent", + "//submodules/Components/SolidRoundedButtonComponent:SolidRoundedButtonComponent", + "//submodules/TelegramUI/Components/EmojiStatusComponent", ], visibility = [ "//visibility:public", diff --git a/submodules/PremiumUI/Sources/EmojiHeaderComponent.swift b/submodules/PremiumUI/Sources/EmojiHeaderComponent.swift new file mode 100644 index 0000000000..0cdf1c5b93 --- /dev/null +++ b/submodules/PremiumUI/Sources/EmojiHeaderComponent.swift @@ -0,0 +1,322 @@ +import Foundation +import UIKit +import Display +import ComponentFlow +import SwiftSignalKit +import SceneKit +import GZip +import AppBundle +import LegacyComponents +import AvatarNode +import AccountContext +import TelegramCore +import AnimationCache +import MultiAnimationRenderer +import EmojiStatusComponent + +private let sceneVersion: Int = 3 + +class EmojiHeaderComponent: Component { + let context: AccountContext + let animationCache: AnimationCache + let animationRenderer: MultiAnimationRenderer + let placeholderColor: UIColor + let fileId: Int64 + let isVisible: Bool + let hasIdleAnimations: Bool + + init( + context: AccountContext, + animationCache: AnimationCache, + animationRenderer: MultiAnimationRenderer, + placeholderColor: UIColor, + fileId: Int64, + isVisible: Bool, + hasIdleAnimations: Bool + ) { + self.context = context + self.animationCache = animationCache + self.animationRenderer = animationRenderer + self.placeholderColor = placeholderColor + self.fileId = fileId + self.isVisible = isVisible + self.hasIdleAnimations = hasIdleAnimations + } + + static func ==(lhs: EmojiHeaderComponent, rhs: EmojiHeaderComponent) -> Bool { + return lhs.placeholderColor == rhs.placeholderColor && lhs.fileId == rhs.fileId && lhs.isVisible == rhs.isVisible && lhs.hasIdleAnimations == rhs.hasIdleAnimations + } + + final class View: UIView, SCNSceneRendererDelegate, ComponentTaggedView { + final class Tag { + } + + func matches(tag: Any) -> Bool { + if let _ = tag as? Tag { + return true + } + return false + } + + private var _ready = Promise() + var ready: Signal { + return self._ready.get() + } + + weak var animateFrom: UIView? + weak var containerView: UIView? + var animationColor: UIColor? + + private let sceneView: SCNView + let statusView: ComponentHostView + + private var previousInteractionTimestamp: Double = 0.0 + private var timer: SwiftSignalKit.Timer? + private var hasIdleAnimations = false + + override init(frame: CGRect) { + self.sceneView = SCNView(frame: CGRect(origin: .zero, size: CGSize(width: 64.0, height: 64.0))) + self.sceneView.backgroundColor = .clear + self.sceneView.transform = CGAffineTransform(scaleX: 0.5, y: 0.5) + self.sceneView.isUserInteractionEnabled = false + self.sceneView.preferredFramesPerSecond = 60 + + self.statusView = ComponentHostView() + + super.init(frame: frame) + + self.addSubview(self.sceneView) + self.addSubview(self.statusView) + + self.setup() + + let tapGestureRecoginzer = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:))) + self.addGestureRecognizer(tapGestureRecoginzer) + + self.disablesInteractiveModalDismiss = true + self.disablesInteractiveTransitionGestureRecognizer = true + } + + required init?(coder: NSCoder) { + fatalError("init(coder:) has not been implemented") + } + + deinit { + self.timer?.invalidate() + } + + private let hapticFeedback = HapticFeedback() + + private var delayTapsTill: Double? + @objc private func handleTap(_ gesture: UITapGestureRecognizer) { + self.playAppearanceAnimation(velocity: nil, mirror: false, explode: true) + } + + private func setup() { + guard let url = getAppBundle().url(forResource: "gift", withExtension: "scn"), let scene = try? SCNScene(url: url, options: nil) else { + return + } + + self.sceneView.scene = scene + self.sceneView.delegate = self + + let _ = self.sceneView.snapshot() + } + + private var didSetReady = false + func renderer(_ renderer: SCNSceneRenderer, didRenderScene scene: SCNScene, atTime time: TimeInterval) { + if !self.didSetReady { + self.didSetReady = true + + Queue.mainQueue().justDispatch { + self._ready.set(.single(true)) + self.onReady() + } + } + } + + private func maybeAnimateIn() { + guard let animateFrom = self.animateFrom, var containerView = self.containerView else { + return + } + + containerView = containerView.subviews[2].subviews[1] + + let initialPosition = self.statusView.center + let targetPosition = self.statusView.superview!.convert(self.statusView.center, to: containerView) + let sourcePosition = animateFrom.superview!.convert(animateFrom.center, to: containerView).offsetBy(dx: 0.0, dy: -20.0) + + containerView.addSubview(self.statusView) + self.statusView.center = targetPosition + + animateFrom.alpha = 0.0 + self.statusView.layer.animateScale(from: 0.05, to: 1.0, duration: 0.8, timingFunction: kCAMediaTimingFunctionSpring) + self.statusView.layer.animatePosition(from: sourcePosition, to: targetPosition, duration: 0.8, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in + self.addSubview(self.statusView) + self.statusView.center = initialPosition + }) + + Queue.mainQueue().after(0.4, { + animateFrom.alpha = 1.0 + }) + + self.animateFrom = nil + self.containerView = nil + } + + private func onReady() { + self.setupScaleAnimation() + + self.maybeAnimateIn() + self.playAppearanceAnimation(explode: true) + + self.previousInteractionTimestamp = CACurrentMediaTime() + self.timer = SwiftSignalKit.Timer(timeout: 1.0, repeat: true, completion: { [weak self] in + if let strongSelf = self, strongSelf.hasIdleAnimations { + let currentTimestamp = CACurrentMediaTime() + if currentTimestamp > strongSelf.previousInteractionTimestamp + 5.0 { + strongSelf.playAppearanceAnimation() + } + } + }, queue: Queue.mainQueue()) + self.timer?.start() + } + + private func setupScaleAnimation() { +// let animation = CABasicAnimation(keyPath: "transform.scale") +// animation.duration = 2.0 +// animation.fromValue = 1.0 +// animation.toValue = 1.15 +// animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) +// animation.autoreverses = true +// animation.repeatCount = .infinity +// +// self.avatarNode.view.layer.add(animation, forKey: "scale") + } + + private func playAppearanceAnimation(velocity: CGFloat? = nil, smallAngle: Bool = false, mirror: Bool = false, explode: Bool = false) { + guard let scene = self.sceneView.scene else { + return + } + + let currentTime = CACurrentMediaTime() + self.previousInteractionTimestamp = currentTime + self.delayTapsTill = currentTime + 0.85 + + if explode, let node = scene.rootNode.childNode(withName: "swirl", recursively: false), let particlesLeft = scene.rootNode.childNode(withName: "particles_left", recursively: false), let particlesRight = scene.rootNode.childNode(withName: "particles_right", recursively: false), let particlesBottomLeft = scene.rootNode.childNode(withName: "particles_left_bottom", recursively: false), let particlesBottomRight = scene.rootNode.childNode(withName: "particles_right_bottom", recursively: false) { + if let leftParticleSystem = particlesLeft.particleSystems?.first, let rightParticleSystem = particlesRight.particleSystems?.first, let leftBottomParticleSystem = particlesBottomLeft.particleSystems?.first, let rightBottomParticleSystem = particlesBottomRight.particleSystems?.first { + leftParticleSystem.speedFactor = 2.0 + leftParticleSystem.particleVelocity = 1.6 + leftParticleSystem.birthRate = 60.0 + leftParticleSystem.particleLifeSpan = 4.0 + + rightParticleSystem.speedFactor = 2.0 + rightParticleSystem.particleVelocity = 1.6 + rightParticleSystem.birthRate = 60.0 + rightParticleSystem.particleLifeSpan = 4.0 + +// leftBottomParticleSystem.speedFactor = 2.0 + leftBottomParticleSystem.particleVelocity = 1.6 + leftBottomParticleSystem.birthRate = 24.0 + leftBottomParticleSystem.particleLifeSpan = 7.0 + +// rightBottomParticleSystem.speedFactor = 2.0 + rightBottomParticleSystem.particleVelocity = 1.6 + rightBottomParticleSystem.birthRate = 24.0 + rightBottomParticleSystem.particleLifeSpan = 7.0 + + node.physicsField?.isActive = true + Queue.mainQueue().after(1.0) { + node.physicsField?.isActive = false + + leftParticleSystem.birthRate = 12.0 + leftParticleSystem.particleVelocity = 1.2 + leftParticleSystem.particleLifeSpan = 3.0 + + rightParticleSystem.birthRate = 12.0 + rightParticleSystem.particleVelocity = 1.2 + rightParticleSystem.particleLifeSpan = 3.0 + + leftBottomParticleSystem.particleVelocity = 1.2 + leftBottomParticleSystem.birthRate = 7.0 + leftBottomParticleSystem.particleLifeSpan = 5.0 + + rightBottomParticleSystem.particleVelocity = 1.2 + rightBottomParticleSystem.birthRate = 7.0 + rightBottomParticleSystem.particleLifeSpan = 5.0 + + let leftAnimation = POPBasicAnimation() + leftAnimation.property = (POPAnimatableProperty.property(withName: "speedFactor", initializer: { property in + property?.readBlock = { particleSystem, values in + values?.pointee = (particleSystem as! SCNParticleSystem).speedFactor + } + property?.writeBlock = { particleSystem, values in + (particleSystem as! SCNParticleSystem).speedFactor = values!.pointee + } + property?.threshold = 0.01 + }) as! POPAnimatableProperty) + leftAnimation.fromValue = 1.2 as NSNumber + leftAnimation.toValue = 0.85 as NSNumber + leftAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear) + leftAnimation.duration = 0.5 + leftParticleSystem.pop_add(leftAnimation, forKey: "speedFactor") + + let rightAnimation = POPBasicAnimation() + rightAnimation.property = (POPAnimatableProperty.property(withName: "speedFactor", initializer: { property in + property?.readBlock = { particleSystem, values in + values?.pointee = (particleSystem as! SCNParticleSystem).speedFactor + } + property?.writeBlock = { particleSystem, values in + (particleSystem as! SCNParticleSystem).speedFactor = values!.pointee + } + property?.threshold = 0.01 + }) as! POPAnimatableProperty) + rightAnimation.fromValue = 1.2 as NSNumber + rightAnimation.toValue = 0.85 as NSNumber + rightAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear) + rightAnimation.duration = 0.5 + rightParticleSystem.pop_add(rightAnimation, forKey: "speedFactor") + } + } + } + } + + func update(component: EmojiHeaderComponent, availableSize: CGSize, transition: Transition) -> CGSize { + self.sceneView.bounds = CGRect(origin: .zero, size: CGSize(width: availableSize.width * 2.0, height: availableSize.height * 2.0)) + if self.sceneView.superview == self { + self.sceneView.center = CGPoint(x: availableSize.width / 2.0, y: availableSize.height / 2.0) + } + + self.hasIdleAnimations = component.hasIdleAnimations + + let size = self.statusView.update( + transition: .immediate, + component: AnyComponent(EmojiStatusComponent( + context: component.context, + animationCache: component.animationCache, + animationRenderer: component.animationRenderer, + content: .emojiStatus( + status: PeerEmojiStatus(fileId: component.fileId), + size: CGSize(width: 100.0, height: 100.0), + placeholderColor: component.placeholderColor + ), + action: nil, + longTapAction: nil + )), + environment: {}, + containerSize: CGSize(width: 96.0, height: 96.0) + ) + self.statusView.frame = CGRect(origin: CGPoint(x: floorToScreenPixels((availableSize.width - size.width) / 2.0), y: 63.0), size: size) + + return availableSize + } + } + + func makeView() -> View { + return View(frame: CGRect()) + } + + func update(view: View, availableSize: CGSize, state: EmptyComponentState, environment: Environment, transition: Transition) -> CGSize { + return view.update(component: self, availableSize: availableSize, transition: transition) + } +} diff --git a/submodules/PremiumUI/Sources/GiftAvatarComponent.swift b/submodules/PremiumUI/Sources/GiftAvatarComponent.swift index c49f4f0721..094a06bee5 100644 --- a/submodules/PremiumUI/Sources/GiftAvatarComponent.swift +++ b/submodules/PremiumUI/Sources/GiftAvatarComponent.swift @@ -13,41 +13,6 @@ import TelegramCore private let sceneVersion: Int = 3 -private func deg2rad(_ number: Float) -> Float { - return number * .pi / 180 -} - -private func rad2deg(_ number: Float) -> Float { - return number * 180.0 / .pi -} - -private func generateParticlesTexture() -> UIImage { - return UIImage() -} - -private func generateFlecksTexture() -> UIImage { - return UIImage() -} - -private func generateShineTexture() -> UIImage { - return UIImage() -} - -private func generateDiffuseTexture() -> UIImage { - return generateImage(CGSize(width: 256, height: 256), rotatedContext: { size, context in - let colorsArray: [CGColor] = [ - UIColor(rgb: 0x0079ff).cgColor, - UIColor(rgb: 0x6a93ff).cgColor, - UIColor(rgb: 0x9172fe).cgColor, - UIColor(rgb: 0xe46acd).cgColor, - ] - var locations: [CGFloat] = [0.0, 0.25, 0.5, 0.75, 1.0] - let gradient = CGGradient(colorsSpace: deviceColorSpace, colors: colorsArray as CFArray, locations: &locations)! - - context.drawLinearGradient(gradient, start: CGPoint(x: 0.0, y: 0.0), end: CGPoint(x: size.width, y: size.height), options: CGGradientDrawingOptions()) - })! -} - class GiftAvatarComponent: Component { let context: AccountContext let peer: EnginePeer? @@ -174,8 +139,8 @@ class GiftAvatarComponent: Component { private func setupScaleAnimation() { let animation = CABasicAnimation(keyPath: "transform.scale") animation.duration = 2.0 - animation.fromValue = 1.0 //NSValue(scnVector3: SCNVector3(x: 0.1, y: 0.1, z: 0.1)) - animation.toValue = 1.15 //NSValue(scnVector3: SCNVector3(x: 0.115, y: 0.115, z: 0.115)) + animation.fromValue = 1.0 + animation.toValue = 1.15 animation.timingFunction = CAMediaTimingFunction(name: .easeInEaseOut) animation.autoreverses = true animation.repeatCount = .infinity diff --git a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift index d37cc7a008..dfd58e170d 100644 --- a/submodules/PremiumUI/Sources/PremiumIntroScreen.swift +++ b/submodules/PremiumUI/Sources/PremiumIntroScreen.swift @@ -11,6 +11,7 @@ import ViewControllerComponent import AccountContext import SolidRoundedButtonComponent import MultilineTextComponent +import MultilineTextWithEntitiesComponent import BundleIconComponent import SolidRoundedButtonComponent import Markdown @@ -20,6 +21,8 @@ import TextFormat import InstantPageCache import UniversalMediaPlayer import CheckNode +import AnimationCache +import MultiAnimationRenderer public enum PremiumSource: Equatable { case settings @@ -40,7 +43,7 @@ public enum PremiumSource: Equatable { case animatedEmoji case deeplink(String?) case profile(PeerId) - case emojiStatus(PeerId, Int64) + case emojiStatus(PeerId, Int64, TelegramMediaFile?, String?) case gift(from: PeerId, to: PeerId, duration: Int32) case giftTerms @@ -158,7 +161,7 @@ enum PremiumPerk: CaseIterable { case .animatedUserpics: return "animated_userpics" case .appIcons: - return "app_icon" + return "app_icons" case .animatedEmoji: return "animated_emoji" } @@ -1121,6 +1124,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { size.height += 183.0 + 10.0 + environment.navigationHeight - 56.0 let textColor = theme.list.itemPrimaryTextColor + let accentColor = theme.list.itemAccentColor let titleColor = theme.list.itemPrimaryTextColor let subtitleColor = theme.list.itemSecondaryTextColor let arrowColor = theme.list.disclosureArrowColor @@ -1130,7 +1134,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { let textString: String if case .emojiStatus = context.component.source { - textString = strings.Premium_EmojiStatusText + textString = strings.Premium_EmojiStatusText.replacingOccurrences(of: "#", with: "# ") } else if case .giftTerms = context.component.source { textString = strings.Premium_PersonalDescription } else if let _ = context.component.otherPeerName { @@ -1149,9 +1153,10 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { textString = strings.Premium_Description } - let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: textColor), linkAttribute: { _ in + let markdownAttributes = MarkdownAttributes(body: MarkdownAttributeSet(font: textFont, textColor: textColor), bold: MarkdownAttributeSet(font: boldTextFont, textColor: textColor), link: MarkdownAttributeSet(font: textFont, textColor: accentColor), linkAttribute: { _ in return nil }) + let text = text.update( component: MultilineTextComponent( text: .markdown( @@ -1485,9 +1490,9 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent { return nil } }, - tapAction: { attributes, _ in + tapAction: { [weak environment] attributes, _ in if let url = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] as? String, - let controller = environment.controller() as? PremiumIntroScreen, let navigationController = controller.navigationController as? NavigationController { + let controller = environment?.controller() as? PremiumIntroScreen, let navigationController = controller.navigationController as? NavigationController { if url.hasPrefix("https://apps.apple.com/account/subscriptions") { controller.context.sharedContext.applicationBindings.openSubscriptions() } else if url.hasPrefix("https://") || url.hasPrefix("tg://") { @@ -1636,6 +1641,14 @@ private final class PremiumIntroScreenComponent: CombinedComponent { var isPremium: Bool? var otherPeerName: String? + let animationCache: AnimationCache + let animationRenderer: MultiAnimationRenderer + + var emojiFile: TelegramMediaFile? + var emojiPackTitle: String? + private var emojiFileDisposable: Disposable? + + private var disposable: Disposable? private var paymentDisposable = MetaDisposable() private var activationDisposable = MetaDisposable() @@ -1654,6 +1667,11 @@ private final class PremiumIntroScreenComponent: CombinedComponent { self.present = present self.completion = completion + self.animationCache = AnimationCacheImpl(basePath: context.account.postbox.mediaBox.basePath + "/animation-cache", allocateTempFile: { + return TempBox.shared.tempFile(fileName: "file").path + }) + self.animationRenderer = MultiAnimationRendererImpl() + super.init() let availableProducts: Signal<[InAppPurchaseManager.Product], NoError> @@ -1676,7 +1694,7 @@ private final class PremiumIntroScreenComponent: CombinedComponent { |> map { peer -> String? in return peer?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) } - } else if case let .emojiStatus(peerId, _) = source { + } else if case let .emojiStatus(peerId, _, _, _) = source { otherPeerName = context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: peerId)) |> map { peer -> String? in return peer?.displayTitle(strings: presentationData.strings, displayOrder: presentationData.nameDisplayOrder) @@ -1704,12 +1722,30 @@ private final class PremiumIntroScreenComponent: CombinedComponent { strongSelf.updated(transition: .immediate) } }) + + if case let .emojiStatus(_, emojiFileId, emojiFile, emojiPackTitle) = source { + if let emojiFile = emojiFile { + self.emojiFile = emojiFile + self.emojiPackTitle = emojiPackTitle + self.updated(transition: .immediate) + } else { + self.emojiFileDisposable = (context.engine.stickers.resolveInlineStickers(fileIds: [emojiFileId]) + |> deliverOnMainQueue).start(next: { [weak self] result in + guard let strongSelf = self else { + return + } + strongSelf.emojiFile = result[emojiFileId] + strongSelf.updated(transition: .immediate) + }) + } + } } deinit { self.disposable?.dispose() self.paymentDisposable.dispose() self.activationDisposable.dispose() + self.emojiFileDisposable?.dispose() } func buy() { @@ -1830,10 +1866,11 @@ private final class PremiumIntroScreenComponent: CombinedComponent { let background = Child(Rectangle.self) let scrollContent = Child(ScrollComponent.self) let star = Child(PremiumStarComponent.self) + let emoji = Child(EmojiHeaderComponent.self) let topPanel = Child(BlurredRectangle.self) let topSeparator = Child(Rectangle.self) let title = Child(MultilineTextComponent.self) - let secondaryTitle = Child(MultilineTextComponent.self) + let secondaryTitle = Child(MultilineTextWithEntitiesComponent.self) let bottomPanel = Child(BlurredRectangle.self) let bottomSeparator = Child(Rectangle.self) let button = Child(SolidRoundedButtonComponent.self) @@ -1853,12 +1890,33 @@ private final class PremiumIntroScreenComponent: CombinedComponent { if case .profile = context.component.source { isIntro = false } - - let star = star.update( - component: PremiumStarComponent(isIntro: isIntro, isVisible: starIsVisible, hasIdleAnimations: state.hasIdleAnimations), - availableSize: CGSize(width: min(390.0, context.availableSize.width), height: 220.0), - transition: context.transition - ) + + let header: _UpdatedChildComponent + if case let .emojiStatus(_, fileId, _, _) = context.component.source { + header = emoji.update( + component: EmojiHeaderComponent( + context: context.component.context, + animationCache: state.animationCache, + animationRenderer: state.animationRenderer, + placeholderColor: environment.theme.list.mediaPlaceholderColor, + fileId: fileId, + isVisible: starIsVisible, + hasIdleAnimations: state.hasIdleAnimations + ), + availableSize: CGSize(width: min(390.0, context.availableSize.width), height: 220.0), + transition: context.transition + ) + } else { + header = star.update( + component: PremiumStarComponent( + isIntro: isIntro, + isVisible: starIsVisible, + hasIdleAnimations: state.hasIdleAnimations + ), + availableSize: CGSize(width: min(390.0, context.availableSize.width), height: 220.0), + transition: context.transition + ) + } let topPanel = topPanel.update( component: BlurredRectangle( @@ -1899,7 +1957,12 @@ private final class PremiumIntroScreenComponent: CombinedComponent { ) let textColor = environment.theme.list.itemPrimaryTextColor - let accentColor = UIColor(rgb: 0x597cf5) + let accentColor: UIColor + if case .emojiStatus = context.component.source { + accentColor = environment.theme.list.itemAccentColor + } else { + accentColor = UIColor(rgb: 0x597cf5) + } let textFont = Font.bold(18.0) let boldTextFont = Font.bold(18.0) @@ -1908,10 +1971,12 @@ private final class PremiumIntroScreenComponent: CombinedComponent { return nil }) + var highlightableLinks = false let secondaryTitleText: String if let otherPeerName = state.otherPeerName { - if case .emojiStatus = context.component.source { - secondaryTitleText = environment.strings.Premium_EmojiStatusTitle(otherPeerName, "").string + if case let .emojiStatus(_, _, _, emojiPackTitle) = context.component.source { + highlightableLinks = true + secondaryTitleText = environment.strings.Premium_EmojiStatusTitle(otherPeerName, emojiPackTitle ?? "").string.replacingOccurrences(of: "#", with: " # ") } else if case .profile = context.component.source { secondaryTitleText = environment.strings.Premium_PersonalTitle(otherPeerName).string } else if case let .gift(fromPeerId, _, duration) = context.component.source { @@ -1943,13 +2008,47 @@ private final class PremiumIntroScreenComponent: CombinedComponent { secondaryTitleText = "" } + let secondaryAttributedText = NSMutableAttributedString(attributedString: parseMarkdownIntoAttributedString(secondaryTitleText, attributes: markdownAttributes)) + if let emojiFile = state.emojiFile { + let range = (secondaryAttributedText.string as NSString).range(of: "#") + if range.location != NSNotFound { + secondaryAttributedText.addAttribute(ChatTextInputAttributes.customEmoji, value: ChatTextInputTextCustomEmojiAttribute(stickerPack: nil, fileId: emojiFile.fileId.id, file: emojiFile), range: range) + } + } + let accountContext = context.component.context + let presentController = context.component.present + let secondaryTitle = secondaryTitle.update( - component: MultilineTextComponent( - text: .markdown(text: secondaryTitleText, attributes: markdownAttributes), + component: MultilineTextWithEntitiesComponent( + context: context.component.context, + animationCache: context.state.animationCache, + animationRenderer: context.state.animationRenderer, + placeholderColor: environment.theme.list.mediaPlaceholderColor, + text: .plain(secondaryAttributedText), horizontalAlignment: .center, truncationType: .end, maximumNumberOfLines: 2, - lineSpacing: 0.0 + lineSpacing: 0.0, + highlightAction: highlightableLinks ? { attributes in + if let _ = attributes[NSAttributedString.Key(rawValue: TelegramTextAttributes.URL)] { + return NSAttributedString.Key(rawValue: TelegramTextAttributes.URL) + } else { + return nil + } + } : nil, + tapAction: { [weak state, weak environment] _, _ in + if let emojiFile = state?.emojiFile, let controller = environment?.controller() as? PremiumIntroScreen, let navigationController = controller.navigationController as? NavigationController { + for attribute in emojiFile.attributes { + if case let .CustomEmoji(_, _, packReference) = attribute, let packReference = packReference { + let controller = accountContext.sharedContext.makeStickerPackScreen(context: accountContext, updatedPresentationData: nil, mainStickerPack: packReference, stickerPacks: [packReference], parentNavigationController: navigationController, sendSticker: { _, _, _ in + return false + }) + presentController(controller) + break + } + } + } + } ), availableSize: CGSize(width: context.availableSize.width - 32.0, height: context.availableSize.width), transition: context.transition @@ -2035,8 +2134,8 @@ private final class PremiumIntroScreenComponent: CombinedComponent { titleAlpha = state.otherPeerName != nil ? 0.0 : 1.0 } - context.add(star - .position(CGPoint(x: context.availableSize.width / 2.0, y: topInset + star.size.height / 2.0 - 30.0 - titleOffset * titleScale)) + context.add(header + .position(CGPoint(x: context.availableSize.width / 2.0, y: topInset + header.size.height / 2.0 - 30.0 - titleOffset * titleScale)) .scale(titleScale) ) @@ -2251,6 +2350,19 @@ public final class PremiumIntroScreen: ViewControllerComponentContainer { self.didSetReady = true self._ready.set(view.ready) + if let sourceView = self.sourceView { + view.animateFrom = sourceView + view.containerView = self.containerView + view.animationColor = self.animationColor + + self.sourceView = nil + self.containerView = nil + self.animationColor = nil + } + } else if let view = self.node.hostView.findTaggedView(tag: EmojiHeaderComponent.View.Tag()) as? EmojiHeaderComponent.View { + self.didSetReady = true + self._ready.set(view.ready) + if let sourceView = self.sourceView { view.animateFrom = sourceView view.containerView = self.containerView diff --git a/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift b/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift index acee247936..85b69a46a8 100644 --- a/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift +++ b/submodules/SolidRoundedButtonNode/Sources/SolidRoundedButtonNode.swift @@ -162,6 +162,8 @@ public final class SolidRoundedButtonNode: ASDisplayNode { } } } + + public var progressType: SolidRoundedButtonProgressType = .fullSize 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 @@ -570,43 +572,91 @@ public final class SolidRoundedButtonNode: ASDisplayNode { self.isUserInteractionEnabled = false + let rotationAnimation = CABasicAnimation(keyPath: "transform.rotation.z") + rotationAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.easeInEaseOut) + rotationAnimation.duration = 1.0 + rotationAnimation.fromValue = NSNumber(value: Float(0.0)) + rotationAnimation.toValue = NSNumber(value: Float.pi * 2.0) + rotationAnimation.repeatCount = Float.infinity + rotationAnimation.timingFunction = CAMediaTimingFunction(name: CAMediaTimingFunctionName.linear) + rotationAnimation.beginTime = 1.0 + 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) + + switch self.progressType { + case .fullSize: + let progressFrame = CGRect(origin: CGPoint(x: floorToScreenPixels(buttonOffset + (buttonWidth - self.buttonHeight) / 2.0), y: 0.0), size: CGSize(width: self.buttonHeight, height: self.buttonHeight)) + progressNode.frame = progressFrame + progressNode.image = generateIndefiniteActivityIndicatorImage(color: self.buttonBackgroundNode.backgroundColor ?? .clear, diameter: self.buttonHeight, lineWidth: 2.0 + UIScreenPixel) + + self.buttonBackgroundNode.layer.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) + + self.insertSubnode(progressNode, at: 0) + case .embedded: + let diameter: CGFloat = self.buttonHeight - 22.0 + let progressFrame = CGRect(origin: CGPoint(x: floorToScreenPixels(buttonOffset + (buttonWidth - diameter) / 2.0), y: floorToScreenPixels((self.buttonHeight - diameter) / 2.0)), size: CGSize(width: diameter, height: diameter)) + progressNode.frame = progressFrame + progressNode.image = generateIndefiniteActivityIndicatorImage(color: self.theme.foregroundColor, diameter: diameter, lineWidth: 3.0) + + self.addSubnode(progressNode) + } + + progressNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + progressNode.layer.add(rotationAnimation, forKey: "progressRotation") 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) + + self.shimmerView?.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false) + self.borderShimmerView?.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false) } + public func transitionFromProgress() { + guard let progressNode = self.progressNode else { + return + } + self.progressNode = nil + + switch self.progressType { + case .fullSize: + self.buttonBackgroundNode.layer.cornerRadius = self.buttonCornerRadius + self.buttonBackgroundNode.layer.animate(from: self.buttonHeight / 2.0 as NSNumber, to: self.buttonCornerRadius as NSNumber, keyPath: "cornerRadius", timingFunction: CAMediaTimingFunctionName.easeInEaseOut.rawValue, duration: 0.2) + self.buttonBackgroundNode.layer.animateFrame(from: progressNode.frame, to: self.bounds, duration: 0.2) + + self.buttonBackgroundNode.alpha = 1.0 + self.buttonBackgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2, removeOnCompletion: false) + case .embedded: + break + } + + progressNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak progressNode, weak self] _ in + progressNode?.removeFromSupernode() + self?.isUserInteractionEnabled = true + }) + + self.titleNode.alpha = 1.0 + self.titleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + + self.subtitleNode.alpha = 1.0 + self.subtitleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + + self.shimmerView?.layer.removeAllAnimations() + self.shimmerView?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + self.borderShimmerView?.layer.removeAllAnimations() + self.borderShimmerView?.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2) + } } public final class SolidRoundedButtonView: UIView { diff --git a/submodules/TelegramUI/Components/EmojiStatusComponent/Sources/EmojiStatusComponent.swift b/submodules/TelegramUI/Components/EmojiStatusComponent/Sources/EmojiStatusComponent.swift index 05f21efbf6..4faec44345 100644 --- a/submodules/TelegramUI/Components/EmojiStatusComponent/Sources/EmojiStatusComponent.swift +++ b/submodules/TelegramUI/Components/EmojiStatusComponent/Sources/EmojiStatusComponent.swift @@ -21,7 +21,7 @@ public final class EmojiStatusComponent: Component { case verified(fillColor: UIColor, foregroundColor: UIColor) case fake(color: UIColor) case scam(color: UIColor) - case emojiStatus(status: PeerEmojiStatus, placeholderColor: UIColor) + case emojiStatus(status: PeerEmojiStatus, size: CGSize, placeholderColor: UIColor) } public let context: AccountContext @@ -30,6 +30,7 @@ public final class EmojiStatusComponent: Component { public let content: Content public let action: (() -> Void)? public let longTapAction: (() -> Void)? + public let emojiFileUpdated: ((TelegramMediaFile?) -> Void)? public init( context: AccountContext, @@ -37,7 +38,8 @@ public final class EmojiStatusComponent: Component { animationRenderer: MultiAnimationRenderer, content: Content, action: (() -> Void)?, - longTapAction: (() -> Void)? + longTapAction: (() -> Void)?, + emojiFileUpdated: ((TelegramMediaFile?) -> Void)? = nil ) { self.context = context self.animationCache = animationCache @@ -45,6 +47,7 @@ public final class EmojiStatusComponent: Component { self.content = content self.action = action self.longTapAction = longTapAction + self.emojiFileUpdated = emojiFileUpdated } public static func ==(lhs: EmojiStatusComponent, rhs: EmojiStatusComponent) -> Bool { @@ -105,6 +108,7 @@ public final class EmojiStatusComponent: Component { var iconImage: UIImage? var emojiFileId: Int64? var emojiPlaceholderColor: UIColor? + var emojiSize = CGSize() /* if case .fake = credibilityIcon { @@ -213,12 +217,13 @@ public final class EmojiStatusComponent: Component { iconImage = nil case .scam: iconImage = nil - case let .emojiStatus(emojiStatus, placeholderColor): + case let .emojiStatus(emojiStatus, size, placeholderColor): iconImage = nil emojiFileId = emojiStatus.fileId emojiPlaceholderColor = placeholderColor + emojiSize = size - if case let .emojiStatus(previousEmojiStatus, _) = self.component?.content { + if case let .emojiStatus(previousEmojiStatus, _, _) = self.component?.content { if previousEmojiStatus.fileId != emojiStatus.fileId { self.emojiFileDisposable?.dispose() self.emojiFileDisposable = nil @@ -237,9 +242,10 @@ public final class EmojiStatusComponent: Component { } } else { iconImage = self.iconView?.image - if case let .emojiStatus(status, placeholderColor) = component.content { + if case let .emojiStatus(status, size, placeholderColor) = component.content { emojiFileId = status.fileId emojiPlaceholderColor = placeholderColor + emojiSize = size } } @@ -273,6 +279,7 @@ public final class EmojiStatusComponent: Component { } } + let emojiFileUpdated = component.emojiFileUpdated if let emojiFileId = emojiFileId, let emojiPlaceholderColor = emojiPlaceholderColor { size = availableSize @@ -292,7 +299,7 @@ public final class EmojiStatusComponent: Component { cache: component.animationCache, renderer: component.animationRenderer, placeholderColor: emojiPlaceholderColor, - pointSize: CGSize(width: 32.0, height: 32.0) + pointSize: emojiSize ) self.animationLayer = animationLayer self.layer.addSublayer(animationLayer) @@ -311,10 +318,17 @@ public final class EmojiStatusComponent: Component { } strongSelf.emojiFile = result[emojiFileId] strongSelf.state?.updated(transition: .immediate) + + emojiFileUpdated?(result[emojiFileId]) }) } } } else { + if let _ = self.emojiFile { + self.emojiFile = nil + emojiFileUpdated?(nil) + } + self.emojiFileDisposable?.dispose() self.emojiFileDisposable = nil diff --git a/submodules/TelegramUI/Sources/AuthorizationSequenceCodeEntryController.swift b/submodules/TelegramUI/Sources/AuthorizationSequenceCodeEntryController.swift index 69f7f9c8cb..f80e1ef5f0 100644 --- a/submodules/TelegramUI/Sources/AuthorizationSequenceCodeEntryController.swift +++ b/submodules/TelegramUI/Sources/AuthorizationSequenceCodeEntryController.swift @@ -30,12 +30,12 @@ final class AuthorizationSequenceCodeEntryController: ViewController { var inProgress: Bool = false { didSet { - if self.inProgress { - let item = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(color: self.theme.rootController.navigationBar.accentTextColor)) - self.navigationItem.rightBarButtonItem = item - } else { - self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.strings.Common_Next, style: .done, target: self, action: #selector(self.nextPressed)) - } +// if self.inProgress { +// let item = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(color: self.theme.rootController.navigationBar.accentTextColor)) +// self.navigationItem.rightBarButtonItem = item +// } else { +// self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.strings.Common_Next, style: .done, target: self, action: #selector(self.nextPressed)) +// } self.controllerNode.inProgress = self.inProgress } } diff --git a/submodules/TelegramUI/Sources/AuthorizationSequenceCodeEntryControllerNode.swift b/submodules/TelegramUI/Sources/AuthorizationSequenceCodeEntryControllerNode.swift index 7be80cd0c1..184f52a1ef 100644 --- a/submodules/TelegramUI/Sources/AuthorizationSequenceCodeEntryControllerNode.swift +++ b/submodules/TelegramUI/Sources/AuthorizationSequenceCodeEntryControllerNode.swift @@ -13,6 +13,7 @@ import PhoneNumberFormat import AnimatedStickerNode import TelegramAnimatedStickerNode import SolidRoundedButtonNode +import InvisibleInkDustNode final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextFieldDelegate { private let strings: PresentationStrings @@ -21,7 +22,9 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF private let animationNode: AnimatedStickerNode private let titleNode: ImmediateTextNode private let titleIconNode: ASImageNode - private let currentOptionNode: ASTextNode + private let currentOptionNode: ImmediateTextNode + private var dustNode: InvisibleInkDustNode? + private let currentOptionInfoNode: ASTextNode private let nextOptionTitleNode: ImmediateTextNode private let nextOptionButtonNode: HighlightableButtonNode @@ -84,9 +87,11 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF self.titleIconNode.displayWithoutProcessing = true self.titleIconNode.displaysAsynchronously = false - self.currentOptionNode = ASTextNode() + self.currentOptionNode = ImmediateTextNode() self.currentOptionNode.isUserInteractionEnabled = false self.currentOptionNode.displaysAsynchronously = false + self.currentOptionNode.lineSpacing = 0.1 + self.currentOptionNode.maximumNumberOfLines = 0 self.currentOptionInfoNode = ASTextNode() self.currentOptionInfoNode.isUserInteractionEnabled = false @@ -122,23 +127,6 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF (self.signInWithAppleButton as? ASAuthorizationAppleIDButton)?.cornerRadius = 11 } - /*self.codeField = TextFieldNode() - self.codeField.textField.font = Font.regular(24.0) - self.codeField.textField.textAlignment = .center - if #available(iOSApplicationExtension 10.0, iOS 10.0, *) { - self.codeField.textField.keyboardType = .asciiCapableNumberPad - } else { - self.codeField.textField.keyboardType = .numberPad - } - if #available(iOSApplicationExtension 12.0, iOS 12.0, *) { - self.codeField.textField.textContentType = .oneTimeCode - } - self.codeField.textField.returnKeyType = .done - self.codeField.textField.textColor = self.theme.list.itemPrimaryTextColor - self.codeField.textField.keyboardAppearance = self.theme.rootController.keyboardColor.keyboardAppearance - self.codeField.textField.disableAutomaticKeyboardHandling = [.forward, .backward] - self.codeField.textField.tintColor = self.theme.list.itemAccentColor*/ - super.init() self.setViewBlock({ @@ -165,11 +153,6 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF strongSelf.textChanged(text: strongSelf.codeInputView.text) } - //self.codeField.textField.delegate = self - //self.codeField.textField.addTarget(self, action: #selector(self.codeFieldTextChanged(_:)), for: .editingChanged) - - //self.codeField.textField.attributedPlaceholder = NSAttributedString(string: strings.Login_Code, font: Font.regular(24.0), textColor: self.theme.list.itemPlaceholderTextColor) - self.nextOptionButtonNode.addTarget(self, action: #selector(self.nextOptionNodePressed), forControlEvents: .touchUpInside) self.signInWithAppleButton?.addTarget(self, action: #selector(self.signInWithApplePressed), for: .touchUpInside) } @@ -189,8 +172,7 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF func updateCode(_ code: String) { self.codeInputView.text = code self.textChanged(text: code) - //self.codeField.textField.text = code - //self.codeFieldTextChanged(self.codeField.textField) + if let codeType = self.codeType { var codeLength: Int32? switch codeType { @@ -228,9 +210,9 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF self.currentOptionNode.attributedText = authorizationCurrentOptionText(codeType, strings: self.strings, primaryColor: self.theme.list.itemPrimaryTextColor, accentColor: self.theme.list.itemAccentColor) if case .missedCall = codeType { - self.currentOptionInfoNode.attributedText = NSAttributedString(string: self.strings.Login_CodePhonePatternInfoText, font: Font.regular(16.0), textColor: self.theme.list.itemPrimaryTextColor, paragraphAlignment: .center) + self.currentOptionInfoNode.attributedText = NSAttributedString(string: self.strings.Login_CodePhonePatternInfoText, font: Font.regular(17.0), textColor: self.theme.list.itemPrimaryTextColor, paragraphAlignment: .center) } else { - self.currentOptionInfoNode.attributedText = NSAttributedString(string: "", font: Font.regular(15.0), textColor: self.theme.list.itemPrimaryTextColor) + self.currentOptionInfoNode.attributedText = NSAttributedString(string: "", font: Font.regular(17.0), textColor: self.theme.list.itemPrimaryTextColor) } if let timeout = timeout { #if DEBUG @@ -272,13 +254,8 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF self.layoutArguments = (layout, navigationBarHeight) var insets = layout.insets(options: []) - insets.top = navigationBarHeight - - if let inputHeight = layout.inputHeight { - insets.bottom += max(inputHeight, layout.standardInputHeight) - } - - + insets.top = layout.statusBarHeight ?? 20.0 + var animationName = "IntroMessage" if let codeType = self.codeType { switch codeType { @@ -296,15 +273,23 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF self.titleNode.attributedText = NSAttributedString(string: self.phoneNumber, font: Font.semibold(40.0), textColor: self.theme.list.itemPrimaryTextColor) } + if let inputHeight = layout.inputHeight { + if let codeType = self.codeType, case .email = codeType { + insets.bottom = max(inputHeight, insets.bottom) + } else { + insets.bottom = max(inputHeight, layout.standardInputHeight) + } + } + if !self.animationNode.visibility { - self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: animationName), width: 256, height: 256, playbackMode: .loop, mode: .direct(cachePathPrefix: nil)) + self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: animationName), width: 256, height: 256, playbackMode: .once, mode: .direct(cachePathPrefix: nil)) self.animationNode.visibility = true } let animationSize = CGSize(width: 88.0, height: 88.0) let titleSize = self.titleNode.updateLayout(CGSize(width: layout.size.width, height: CGFloat.greatestFiniteMagnitude)) - let currentOptionSize = self.currentOptionNode.measure(CGSize(width: layout.size.width - 28.0, height: CGFloat.greatestFiniteMagnitude)) + let currentOptionSize = self.currentOptionNode.updateLayout(CGSize(width: layout.size.width - 28.0, height: CGFloat.greatestFiniteMagnitude)) let currentOptionInfoSize = self.currentOptionInfoNode.measure(CGSize(width: layout.size.width - 28.0, height: CGFloat.greatestFiniteMagnitude)) let nextOptionSize = self.nextOptionTitleNode.updateLayout(CGSize(width: layout.size.width, height: CGFloat.greatestFiniteMagnitude)) @@ -347,9 +332,10 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF ) var items: [AuthorizationLayoutItem] = [] - items.append(AuthorizationLayoutItem(node: self.animationNode, size: animationSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) + items.append(AuthorizationLayoutItem(node: self.animationNode, size: animationSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 10.0, maxValue: 10.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) self.animationNode.updateLayout(size: animationSize) + var additionalBottomInset: CGFloat = 20.0 if let codeType = self.codeType { switch codeType { case .otherSession: @@ -421,22 +407,25 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF items.append(AuthorizationLayoutItem(node: self.nextOptionButtonNode, size: nextOptionSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 50.0, maxValue: 120.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) default: self.titleIconNode.isHidden = true - items.append(AuthorizationLayoutItem(node: self.titleNode, size: titleSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) - items.append(AuthorizationLayoutItem(node: self.currentOptionNode, size: currentOptionSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 10.0, maxValue: 10.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) + items.append(AuthorizationLayoutItem(node: self.titleNode, size: titleSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 18.0, maxValue: 18.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) + items.append(AuthorizationLayoutItem(node: self.currentOptionNode, size: currentOptionSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 18.0, maxValue: 18.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) - items.append(AuthorizationLayoutItem(node: self.codeInputView, size: codeFieldSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 40.0, maxValue: 100.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) + items.append(AuthorizationLayoutItem(node: self.codeInputView, size: codeFieldSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 30.0, maxValue: 30.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 104.0, maxValue: 104.0))) /*items.append(AuthorizationLayoutItem(node: self.codeField, size: CGSize(width: layout.size.width - 88.0, height: 44.0), spacingBefore: AuthorizationLayoutItemSpacing(weight: 40.0, maxValue: 100.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) items.append(AuthorizationLayoutItem(node: self.codeSeparatorNode, size: CGSize(width: layout.size.width - 88.0, height: UIScreenPixel), spacingBefore: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)))*/ if self.appleSignInAllowed, let signInWithAppleButton = self.signInWithAppleButton { + additionalBottomInset = 80.0 + self.nextOptionButtonNode.isHidden = true signInWithAppleButton.isHidden = false + + let inset: CGFloat = 24.0 + let buttonSize = CGSize(width: layout.size.width - inset * 2.0, height: 50.0) + transition.updateFrame(view: signInWithAppleButton, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - buttonSize.width) / 2.0), y: layout.size.height - insets.bottom - buttonSize.height - inset), size: buttonSize)) let dividerSize = self.dividerNode.updateLayout(width: layout.size.width) - items.append(AuthorizationLayoutItem(node: self.dividerNode, size: dividerSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 50.0, maxValue: 120.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) - - let buttonSize = CGSize(width: layout.size.width - 48.0, height: 50.0) - items.append(AuthorizationLayoutItem(view: signInWithAppleButton, size: buttonSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 10.0, maxValue: 10.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) + transition.updateFrame(node: self.dividerNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - dividerSize.width) / 2.0), y: layout.size.height - insets.bottom - buttonSize.height - inset - dividerSize.height), size: dividerSize)) } else { self.signInWithAppleButton?.isHidden = true self.dividerNode.isHidden = true @@ -450,13 +439,28 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF items.append(AuthorizationLayoutItem(node: self.currentOptionNode, size: currentOptionSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 10.0, maxValue: 10.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) items.append(AuthorizationLayoutItem(node: self.codeInputView, size: codeFieldSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 40.0, maxValue: 100.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) - /*items.append(AuthorizationLayoutItem(node: self.codeField, size: CGSize(width: layout.size.width - 88.0, height: 44.0), spacingBefore: AuthorizationLayoutItemSpacing(weight: 40.0, maxValue: 100.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) - items.append(AuthorizationLayoutItem(node: self.codeSeparatorNode, size: CGSize(width: layout.size.width - 88.0, height: UIScreenPixel), spacingBefore: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)))*/ items.append(AuthorizationLayoutItem(node: self.nextOptionButtonNode, size: nextOptionSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 50.0, maxValue: 120.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) } - let _ = layoutAuthorizationItems(bounds: CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: layout.size.height - insets.top - insets.bottom - 20.0)), items: items, transition: transition, failIfDoesNotFit: false) + let _ = layoutAuthorizationItems(bounds: CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: layout.size.height - insets.top - insets.bottom - additionalBottomInset)), items: items, transition: transition, failIfDoesNotFit: false) + + if let textLayout = self.currentOptionNode.cachedLayout, !textLayout.spoilers.isEmpty { + if self.dustNode == nil { + let dustNode = InvisibleInkDustNode(textNode: nil) + self.dustNode = dustNode + self.currentOptionNode.supernode?.insertSubnode(dustNode, aboveSubnode: self.currentOptionNode) + + } + if let dustNode = self.dustNode { + let textFrame = self.currentOptionNode.frame + dustNode.update(size: textFrame.size, color: self.theme.list.itemSecondaryTextColor, textColor: self.theme.list.itemPrimaryTextColor, rects: textLayout.spoilers.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) }, wordRects: textLayout.spoilerWords.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) }) + transition.updateFrame(node: dustNode, frame: textFrame.insetBy(dx: -3.0, dy: -3.0).offsetBy(dx: 0.0, dy: 3.0)) + } + } else if let dustNode = self.dustNode { + self.dustNode = nil + dustNode.removeFromSupernode() + } self.nextOptionTitleNode.frame = self.nextOptionButtonNode.bounds } diff --git a/submodules/TelegramUI/Sources/AuthorizationSequenceEmailEntryControllerNode.swift b/submodules/TelegramUI/Sources/AuthorizationSequenceEmailEntryControllerNode.swift index 7a9a1bd7e7..25dbef379e 100644 --- a/submodules/TelegramUI/Sources/AuthorizationSequenceEmailEntryControllerNode.swift +++ b/submodules/TelegramUI/Sources/AuthorizationSequenceEmailEntryControllerNode.swift @@ -84,7 +84,7 @@ final class AuthorizationSequenceEmailEntryControllerNode: ASDisplayNode, UIText self.theme = theme self.animationNode = DefaultAnimatedStickerNodeImpl() - self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "IntroMail"), width: 256, height: 256, playbackMode: .loop, mode: .direct(cachePathPrefix: nil)) + self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "IntroMail"), width: 256, height: 256, playbackMode: .once, mode: .direct(cachePathPrefix: nil)) self.animationNode.visibility = true self.titleNode = ASTextNode() @@ -95,6 +95,7 @@ final class AuthorizationSequenceEmailEntryControllerNode: ASDisplayNode, UIText self.noticeNode = ASTextNode() self.noticeNode.isUserInteractionEnabled = false self.noticeNode.displaysAsynchronously = false + self.noticeNode.lineSpacing = 0.1 self.noticeNode.attributedText = NSAttributedString(string: "Please enter your valid email address to protect your account.", font: Font.regular(16.0), textColor: self.theme.list.itemPrimaryTextColor, paragraphAlignment: .center) if #available(iOS 13.0, *) { @@ -186,13 +187,13 @@ final class AuthorizationSequenceEmailEntryControllerNode: ASDisplayNode, UIText self.layoutArguments = (layout, navigationBarHeight) var insets = layout.insets(options: []) - insets.top = navigationBarHeight + insets.top = layout.statusBarHeight ?? 20.0 if let inputHeight = layout.inputHeight { - insets.bottom += max(inputHeight, layout.standardInputHeight) + insets.bottom += max(inputHeight, insets.bottom) } - self.titleNode.attributedText = NSAttributedString(string: "Add Email", font: Font.semibold(28.0), textColor: self.theme.list.itemPrimaryTextColor) + self.titleNode.attributedText = NSAttributedString(string: "Add Email", font: Font.bold(28.0), textColor: self.theme.list.itemPrimaryTextColor) let animationSize = CGSize(width: 88.0, height: 88.0) let titleSize = self.titleNode.measure(CGSize(width: layout.size.width, height: CGFloat.greatestFiniteMagnitude)) @@ -202,11 +203,11 @@ final class AuthorizationSequenceEmailEntryControllerNode: ASDisplayNode, UIText let proceedSize = CGSize(width: layout.size.width - 48.0, height: proceedHeight) var items: [AuthorizationLayoutItem] = [] - items.append(AuthorizationLayoutItem(node: self.animationNode, size: animationSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) + items.append(AuthorizationLayoutItem(node: self.animationNode, size: animationSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 10.0, maxValue: 10.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) self.animationNode.updateLayout(size: animationSize) items.append(AuthorizationLayoutItem(node: self.titleNode, size: titleSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 18.0, maxValue: 18.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) - items.append(AuthorizationLayoutItem(node: self.noticeNode, size: noticeSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 20.0, maxValue: 20.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) + items.append(AuthorizationLayoutItem(node: self.noticeNode, size: noticeSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 18.0, maxValue: 18.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) items.append(AuthorizationLayoutItem(node: self.codeField, size: CGSize(width: layout.size.width - 88.0, height: 44.0), spacingBefore: AuthorizationLayoutItemSpacing(weight: 32.0, maxValue: 60.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) items.append(AuthorizationLayoutItem(node: self.codeSeparatorNode, size: CGSize(width: layout.size.width - 48.0, height: UIScreenPixel), spacingBefore: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) @@ -221,7 +222,7 @@ final class AuthorizationSequenceEmailEntryControllerNode: ASDisplayNode, UIText items.append(AuthorizationLayoutItem(node: self.proceedNode, size: proceedSize, spacingBefore: self.dividerNode.isHidden ? AuthorizationLayoutItemSpacing(weight: 48.0, maxValue: 100.0) : AuthorizationLayoutItemSpacing(weight: 10.0, maxValue: 10.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) - let _ = layoutAuthorizationItems(bounds: CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: layout.size.height - insets.top - insets.bottom - 20.0)), items: items, transition: transition, failIfDoesNotFit: false) + let _ = layoutAuthorizationItems(bounds: CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: layout.size.height - insets.top - insets.bottom - 80.0)), items: items, transition: transition, failIfDoesNotFit: false) if let signInWithAppleButton = self.signInWithAppleButton, self.appleSignInAllowed { signInWithAppleButton.isHidden = false diff --git a/submodules/TelegramUI/Sources/AuthorizationSequencePasswordEntryControllerNode.swift b/submodules/TelegramUI/Sources/AuthorizationSequencePasswordEntryControllerNode.swift index 150e8504c4..b1d2f1be83 100644 --- a/submodules/TelegramUI/Sources/AuthorizationSequencePasswordEntryControllerNode.swift +++ b/submodules/TelegramUI/Sources/AuthorizationSequencePasswordEntryControllerNode.swift @@ -50,7 +50,8 @@ final class AuthorizationSequencePasswordEntryControllerNode: ASDisplayNode, UIT self.noticeNode = ASTextNode() self.noticeNode.isUserInteractionEnabled = false self.noticeNode.displaysAsynchronously = false - self.noticeNode.attributedText = NSAttributedString(string: strings.TwoStepAuth_EnterPasswordHelp, font: Font.regular(16.0), textColor: self.theme.list.itemPrimaryTextColor, paragraphAlignment: .center) + self.noticeNode.lineSpacing = 0.1 + self.noticeNode.attributedText = NSAttributedString(string: strings.TwoStepAuth_EnterPasswordHelp, font: Font.regular(17.0), textColor: self.theme.list.itemPrimaryTextColor, paragraphAlignment: .center) self.forgotNode = HighlightableButtonNode() self.forgotNode.displaysAsynchronously = false diff --git a/submodules/TelegramUI/Sources/AuthorizationSequencePasswordRecoveryControllerNode.swift b/submodules/TelegramUI/Sources/AuthorizationSequencePasswordRecoveryControllerNode.swift index 3584d7ef7a..e4efca8e13 100644 --- a/submodules/TelegramUI/Sources/AuthorizationSequencePasswordRecoveryControllerNode.swift +++ b/submodules/TelegramUI/Sources/AuthorizationSequencePasswordRecoveryControllerNode.swift @@ -43,7 +43,8 @@ final class AuthorizationSequencePasswordRecoveryControllerNode: ASDisplayNode, self.noticeNode = ASTextNode() self.noticeNode.isUserInteractionEnabled = false self.noticeNode.displaysAsynchronously = false - self.noticeNode.attributedText = NSAttributedString(string: strings.TwoStepAuth_RecoveryCodeHelp, font: Font.regular(16.0), textColor: self.theme.list.itemPrimaryTextColor, paragraphAlignment: .center) + self.noticeNode.attributedText = NSAttributedString(string: strings.TwoStepAuth_RecoveryCodeHelp, font: Font.regular(17.0), textColor: self.theme.list.itemPrimaryTextColor, paragraphAlignment: .center) + self.noticeNode.lineSpacing = 0.1 self.noAccessNode = HighlightableButtonNode() self.noAccessNode.displaysAsynchronously = false diff --git a/submodules/TelegramUI/Sources/AuthorizationSequencePhoneEntryController.swift b/submodules/TelegramUI/Sources/AuthorizationSequencePhoneEntryController.swift index 8b1a7f3647..88f0c99d96 100644 --- a/submodules/TelegramUI/Sources/AuthorizationSequencePhoneEntryController.swift +++ b/submodules/TelegramUI/Sources/AuthorizationSequencePhoneEntryController.swift @@ -32,12 +32,12 @@ final class AuthorizationSequencePhoneEntryController: ViewController { var inProgress: Bool = false { didSet { - if self.inProgress { - let item = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(color: self.presentationData.theme.rootController.navigationBar.accentTextColor)) - self.navigationItem.rightBarButtonItem = item - } else { - self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Next, style: .done, target: self, action: #selector(self.nextPressed)) - } +// if self.inProgress { +// let item = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(color: self.presentationData.theme.rootController.navigationBar.accentTextColor)) +// self.navigationItem.rightBarButtonItem = item +// } else { +// self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Next, style: .done, target: self, action: #selector(self.nextPressed)) +// } self.controllerNode.inProgress = self.inProgress } } diff --git a/submodules/TelegramUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift b/submodules/TelegramUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift index e814836f2f..fc205551fa 100644 --- a/submodules/TelegramUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift +++ b/submodules/TelegramUI/Sources/AuthorizationSequencePhoneEntryControllerNode.swift @@ -29,25 +29,27 @@ private final class PhoneAndCountryNode: ASDisplayNode { init(strings: PresentationStrings, theme: PresentationTheme) { self.strings = strings - let countryButtonBackground = generateImage(CGSize(width: 68.0, height: 67.0), rotatedContext: { size, context in + let inset: CGFloat = 24.0 + + let countryButtonBackground = generateImage(CGSize(width: 136.0, height: 67.0), rotatedContext: { size, context in let arrowSize: CGFloat = 10.0 let lineWidth = UIScreenPixel context.clear(CGRect(origin: CGPoint(), size: size)) context.setStrokeColor(theme.list.itemPlainSeparatorColor.cgColor) context.setLineWidth(lineWidth) - context.move(to: CGPoint(x: 15.0, y: lineWidth / 2.0)) - context.addLine(to: CGPoint(x: size.width, y: lineWidth / 2.0)) + context.move(to: CGPoint(x: inset, y: lineWidth / 2.0)) + context.addLine(to: CGPoint(x: size.width - inset, y: lineWidth / 2.0)) context.strokePath() - context.move(to: CGPoint(x: size.width, y: size.height - arrowSize - lineWidth / 2.0)) - context.addLine(to: CGPoint(x: size.width - 1.0, y: size.height - arrowSize - lineWidth / 2.0)) - context.addLine(to: CGPoint(x: size.width - 1.0 - arrowSize, y: size.height - lineWidth / 2.0)) - context.addLine(to: CGPoint(x: size.width - 1.0 - arrowSize - arrowSize, y: size.height - arrowSize - lineWidth / 2.0)) - context.addLine(to: CGPoint(x: 15.0, y: size.height - arrowSize - lineWidth / 2.0)) + context.move(to: CGPoint(x: size.width - inset, y: size.height - arrowSize - lineWidth / 2.0)) + context.addLine(to: CGPoint(x: 69.0, y: size.height - arrowSize - lineWidth / 2.0)) + context.addLine(to: CGPoint(x: 69.0 - arrowSize, y: size.height - lineWidth / 2.0)) + context.addLine(to: CGPoint(x: 69.0 - arrowSize - arrowSize, y: size.height - arrowSize - lineWidth / 2.0)) + context.addLine(to: CGPoint(x: inset, y: size.height - arrowSize - lineWidth / 2.0)) context.strokePath() - })?.stretchableImage(withLeftCapWidth: 67, topCapHeight: 1) + })?.stretchableImage(withLeftCapWidth: 69, topCapHeight: 1) - let countryButtonHighlightedBackground = generateImage(CGSize(width: 68.0, height: 67.0), rotatedContext: { size, context in + let countryButtonHighlightedBackground = generateImage(CGSize(width: 70.0, height: 67.0), rotatedContext: { size, context in let arrowSize: CGFloat = 10.0 context.clear(CGRect(origin: CGPoint(), size: size)) context.setFillColor(theme.list.itemHighlightedBackgroundColor.cgColor) @@ -58,18 +60,18 @@ private final class PhoneAndCountryNode: ASDisplayNode { context.addLine(to: CGPoint(x: size.width - 1.0 - arrowSize - arrowSize, y: size.height - arrowSize)) context.closePath() context.fillPath() - })?.stretchableImage(withLeftCapWidth: 67, topCapHeight: 2) + })?.stretchableImage(withLeftCapWidth: 69, topCapHeight: 2) let phoneInputBackground = generateImage(CGSize(width: 96.0, height: 57.0), rotatedContext: { size, context in let lineWidth = UIScreenPixel context.clear(CGRect(origin: CGPoint(), size: size)) context.setStrokeColor(theme.list.itemPlainSeparatorColor.cgColor) context.setLineWidth(lineWidth) - context.move(to: CGPoint(x: 15.0, y: size.height - lineWidth / 2.0)) + context.move(to: CGPoint(x: inset, y: size.height - lineWidth / 2.0)) context.addLine(to: CGPoint(x: size.width, y: size.height - lineWidth / 2.0)) context.strokePath() - context.move(to: CGPoint(x: size.width - 2.0 + lineWidth / 2.0, y: size.height - lineWidth / 2.0)) - context.addLine(to: CGPoint(x: size.width - 2.0 + lineWidth / 2.0, y: 0.0)) + context.move(to: CGPoint(x: size.width - 2.0 + lineWidth / 2.0, y: size.height - 9.0)) + context.addLine(to: CGPoint(x: size.width - 2.0 + lineWidth / 2.0, y: 8.0)) context.strokePath() })?.stretchableImage(withLeftCapWidth: 95, topCapHeight: 2) @@ -107,7 +109,7 @@ private final class PhoneAndCountryNode: ASDisplayNode { self.phoneInputNode.countryCodeField.textField.disableAutomaticKeyboardHandling = [.forward] self.phoneInputNode.numberField.textField.disableAutomaticKeyboardHandling = [.forward] - self.countryButton.contentEdgeInsets = UIEdgeInsets(top: 0.0, left: 15.0, bottom: 10.0, right: 0.0) + self.countryButton.contentEdgeInsets = UIEdgeInsets(top: 0.0, left: 24.0 + 16.0, bottom: 10.0, right: 0.0) self.countryButton.contentHorizontalAlignment = .left self.countryButton.addTarget(self, action: #selector(self.countryPressed), forControlEvents: .touchUpInside) @@ -159,7 +161,7 @@ private final class PhoneAndCountryNode: ASDisplayNode { strongSelf.countryButton.setTitle("\(flagString) \(localizedName)", with: Font.regular(20.0), with: theme.list.itemAccentColor, for: []) strongSelf.phoneInputNode.numberField.textField.attributedPlaceholder = NSAttributedString(string: strings.Login_PhonePlaceholder, font: Font.regular(20.0), textColor: theme.list.itemPlaceholderTextColor) } else { - strongSelf.countryButton.setTitle(strings.Login_SelectCountry_Title, with: Font.regular(20.0), with: theme.list.itemAccentColor, for: []) + strongSelf.countryButton.setTitle(strings.Login_SelectCountry, with: Font.regular(20.0), with: theme.list.itemAccentColor, for: []) strongSelf.phoneInputNode.mask = nil strongSelf.phoneInputNode.numberField.textField.attributedPlaceholder = NSAttributedString(string: strings.Login_PhonePlaceholder, font: Font.regular(20.0), textColor: theme.list.itemPlaceholderTextColor) } @@ -188,13 +190,14 @@ private final class PhoneAndCountryNode: ASDisplayNode { super.layout() let size = self.bounds.size + let inset: CGFloat = 24.0 self.countryButton.frame = CGRect(origin: CGPoint(), size: CGSize(width: size.width, height: 67.0)) - self.phoneBackground.frame = CGRect(origin: CGPoint(x: 0.0, y: size.height - 57.0), size: CGSize(width: size.width, height: 57.0)) + self.phoneBackground.frame = CGRect(origin: CGPoint(x: 0.0, y: size.height - 57.0), size: CGSize(width: size.width - inset, height: 57.0)) - let countryCodeFrame = CGRect(origin: CGPoint(x: 18.0, y: size.height - 57.0), size: CGSize(width: 71.0, height: 57.0)) - let numberFrame = CGRect(origin: CGPoint(x: 107.0, y: size.height - 57.0), size: CGSize(width: size.width - 96.0 - 8.0, height: 57.0)) - let placeholderFrame = numberFrame.offsetBy(dx: 0.0, dy: 16.0) + let countryCodeFrame = CGRect(origin: CGPoint(x: 18.0, y: size.height - 58.0), size: CGSize(width: 71.0, height: 57.0)) + let numberFrame = CGRect(origin: CGPoint(x: 107.0, y: size.height - 58.0), size: CGSize(width: size.width - 96.0 - 8.0, height: 57.0)) + let placeholderFrame = numberFrame.offsetBy(dx: 0.0, dy: 17.0 - UIScreenPixel) let phoneInputFrame = countryCodeFrame.union(numberFrame) @@ -227,10 +230,11 @@ private final class ContactSyncNode: ASDisplayNode { func updateLayout(width: CGFloat) -> CGSize { let switchSize = CGSize(width: 51.0, height: 31.0) - let titleSize = self.titleNode.updateLayout(CGSize(width: width - switchSize.width - 16.0 * 2.0 - 8.0, height: .greatestFiniteMagnitude)) + let inset: CGFloat = 24.0 + let titleSize = self.titleNode.updateLayout(CGSize(width: width - switchSize.width - inset * 2.0 - 8.0, height: .greatestFiniteMagnitude)) let height: CGFloat = 40.0 - self.titleNode.frame = CGRect(origin: CGPoint(x: 16.0, y: floor((height - titleSize.height) / 2.0)), size: titleSize) - self.switchNode.frame = CGRect(origin: CGPoint(x: width - 16.0 - switchSize.width, y: floor((height - switchSize.height) / 2.0)), size: switchSize) + self.titleNode.frame = CGRect(origin: CGPoint(x: inset, y: floor((height - titleSize.height) / 2.0)), size: titleSize) + self.switchNode.frame = CGRect(origin: CGPoint(x: width - inset - switchSize.width, y: floor((height - switchSize.height) / 2.0)), size: switchSize) return CGSize(width: width, height: height) } } @@ -286,6 +290,14 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode { self.phoneAndCountryNode.phoneInputNode.enableEditing = !self.inProgress self.phoneAndCountryNode.phoneInputNode.alpha = self.inProgress ? 0.6 : 1.0 self.phoneAndCountryNode.countryButton.isEnabled = !self.inProgress + + if self.inProgress != oldValue { + if self.inProgress { + self.proceedNode.transitionToProgress() + } else { + self.proceedNode.transitionFromProgress() + } + } } } @@ -299,7 +311,7 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode { self.hasOtherAccounts = hasOtherAccounts self.animationNode = DefaultAnimatedStickerNodeImpl() - self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "IntroPhone"), width: 256, height: 256, playbackMode: .loop, mode: .direct(cachePathPrefix: nil)) + self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "IntroPhone"), width: 256, height: 256, playbackMode: .once, mode: .direct(cachePathPrefix: nil)) self.animationNode.visibility = true self.titleNode = ASTextNode() @@ -311,13 +323,15 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode { self.noticeNode.maximumNumberOfLines = 0 self.noticeNode.isUserInteractionEnabled = true self.noticeNode.displaysAsynchronously = false - self.noticeNode.attributedText = NSAttributedString(string: strings.Login_PhoneAndCountryHelp, font: Font.regular(16.0), textColor: theme.list.itemPrimaryTextColor, paragraphAlignment: .center) + self.noticeNode.lineSpacing = 0.1 + self.noticeNode.attributedText = NSAttributedString(string: strings.Login_PhoneAndCountryHelp, font: Font.regular(17.0), textColor: theme.list.itemPrimaryTextColor, paragraphAlignment: .center) self.contactSyncNode = ContactSyncNode(theme: theme, strings: strings) self.phoneAndCountryNode = PhoneAndCountryNode(strings: strings, theme: theme) self.proceedNode = SolidRoundedButtonNode(title: "Continue", theme: SolidRoundedButtonTheme(theme: self.theme), height: 50.0, cornerRadius: 11.0, gloss: false) + self.proceedNode.progressType = .embedded super.init() @@ -372,25 +386,27 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode { func containerLayoutUpdated(_ layout: ContainerViewLayout, navigationBarHeight: CGFloat, transition: ContainedViewLayoutTransition) { var insets = layout.insets(options: []) - insets.top = navigationBarHeight + insets.top = layout.statusBarHeight ?? 20.0 if let inputHeight = layout.inputHeight, !inputHeight.isZero { - insets.bottom += max(inputHeight, layout.standardInputHeight) + insets.bottom = max(inputHeight, insets.bottom) } - self.titleNode.attributedText = NSAttributedString(string: strings.Login_PhoneTitle, font: Font.semibold(28.0), textColor: self.theme.list.itemPrimaryTextColor) + self.titleNode.attributedText = NSAttributedString(string: strings.Login_PhoneTitle, font: Font.bold(28.0), textColor: self.theme.list.itemPrimaryTextColor) + + let inset: CGFloat = 24.0 let animationSize = CGSize(width: 88.0, height: 88.0) let titleSize = self.titleNode.measure(CGSize(width: layout.size.width, height: CGFloat.greatestFiniteMagnitude)) let noticeSize = self.noticeNode.measure(CGSize(width: min(274.0, layout.size.width - 28.0), height: CGFloat.greatestFiniteMagnitude)) - let proceedHeight = self.proceedNode.updateLayout(width: layout.size.width - 48.0, transition: transition) - let proceedSize = CGSize(width: layout.size.width - 48.0, height: proceedHeight) + let proceedHeight = self.proceedNode.updateLayout(width: layout.size.width - inset * 2.0, transition: transition) + let proceedSize = CGSize(width: layout.size.width - inset * 2.0, height: proceedHeight) var items: [AuthorizationLayoutItem] = [ - AuthorizationLayoutItem(node: self.animationNode, size: animationSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)), + AuthorizationLayoutItem(node: self.animationNode, size: animationSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 10.0, maxValue: 10.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)), AuthorizationLayoutItem(node: self.titleNode, size: titleSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 18.0, maxValue: 18.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)), - AuthorizationLayoutItem(node: self.noticeNode, size: noticeSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 20.0, maxValue: 20.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)), - AuthorizationLayoutItem(node: self.phoneAndCountryNode, size: CGSize(width: layout.size.width, height: 115.0), spacingBefore: AuthorizationLayoutItemSpacing(weight: 44.0, maxValue: 44.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)), + AuthorizationLayoutItem(node: self.noticeNode, size: noticeSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 18.0, maxValue: 18.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)), + AuthorizationLayoutItem(node: self.phoneAndCountryNode, size: CGSize(width: layout.size.width, height: 115.0), spacingBefore: AuthorizationLayoutItemSpacing(weight: 30.0, maxValue: 30.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)), ] let contactSyncSize = self.contactSyncNode.updateLayout(width: layout.size.width) if self.hasOtherAccounts { @@ -400,11 +416,11 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode { self.contactSyncNode.isHidden = true } - items.append(AuthorizationLayoutItem(node: self.proceedNode, size: proceedSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 48.0, maxValue: 100.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0))) + transition.updateFrame(node: self.proceedNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - proceedSize.width) / 2.0), y: layout.size.height - insets.bottom - proceedSize.height - inset), size: proceedSize)) self.animationNode.updateLayout(size: animationSize) - let _ = layoutAuthorizationItems(bounds: CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: layout.size.height - insets.top - insets.bottom - 10.0)), items: items, transition: transition, failIfDoesNotFit: false) + let _ = layoutAuthorizationItems(bounds: CGRect(origin: CGPoint(x: 0.0, y: insets.top), size: CGSize(width: layout.size.width, height: layout.size.height - insets.top - insets.bottom - 80.0)), items: items, transition: transition, failIfDoesNotFit: false) } func activateInput() { diff --git a/submodules/TelegramUI/Sources/ChatTitleView.swift b/submodules/TelegramUI/Sources/ChatTitleView.swift index 7caee79b4d..5bf864094d 100644 --- a/submodules/TelegramUI/Sources/ChatTitleView.swift +++ b/submodules/TelegramUI/Sources/ChatTitleView.swift @@ -676,7 +676,7 @@ final class ChatTitleView: UIView, NavigationBarTitleView { case .scam: titleCredibilityContent = .scam(color: self.theme.chat.message.incoming.scamColor) case let .emojiStatus(emojiStatus): - titleCredibilityContent = .emojiStatus(status: emojiStatus, placeholderColor: self.theme.list.mediaPlaceholderColor) + titleCredibilityContent = .emojiStatus(status: emojiStatus, size: CGSize(width: 32.0, height: 32.0), placeholderColor: self.theme.list.mediaPlaceholderColor) } let titleCredibilitySize = self.titleCredibilityIconView.update( diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift index 0034ac650e..1fcdb849c7 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoHeaderNode.swift @@ -2023,7 +2023,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { var displayAvatarContextMenu: ((ASDisplayNode, ContextGesture?) -> Void)? var displayCopyContextMenu: ((ASDisplayNode, Bool, Bool) -> Void)? - var displayPremiumIntro: ((UIView, PeerEmojiStatus?, Bool) -> Void)? + var displayPremiumIntro: ((UIView, PeerEmojiStatus?, TelegramMediaFile?, String?, Bool) -> Void)? var navigationTransition: PeerInfoHeaderNavigationTransition? @@ -2033,6 +2033,10 @@ final class PeerInfoHeaderNode: ASDisplayNode { let animationCache: AnimationCache let animationRenderer: MultiAnimationRenderer + var emojiStatusPackDisposable = MetaDisposable() + var emojiStatusFile: TelegramMediaFile? + var emojiStatusPackTitle: String? + init(context: AccountContext, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, isMediaOnly: Bool, isSettings: Bool) { self.context = context self.isAvatarExpanded = avatarInitiallyExpanded @@ -2185,6 +2189,10 @@ final class PeerInfoHeaderNode: ASDisplayNode { } } + deinit { + self.emojiStatusPackDisposable.dispose() + } + override func didLoad() { super.didLoad() @@ -2214,7 +2222,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { } func invokeDisplayPremiumIntro() { - self.displayPremiumIntro?(self.isAvatarExpanded ? self.titleExpandedCredibilityIconView : self.titleCredibilityIconView, nil, self.isAvatarExpanded) + self.displayPremiumIntro?(self.isAvatarExpanded ? self.titleExpandedCredibilityIconView : self.titleCredibilityIconView, nil, nil, nil, self.isAvatarExpanded) } func initiateAvatarExpansion(gallery: Bool, first: Bool) { @@ -2424,8 +2432,8 @@ final class PeerInfoHeaderNode: ASDisplayNode { emojiExpandedStatusContent = .scam(color: presentationData.theme.chat.message.incoming.scamColor) case let .emojiStatus(emojiStatus): currentEmojiStatus = emojiStatus - emojiRegularStatusContent = .emojiStatus(status: emojiStatus, placeholderColor: presentationData.theme.list.mediaPlaceholderColor) - emojiExpandedStatusContent = .emojiStatus(status: emojiStatus, placeholderColor: UIColor(rgb: 0xffffff, alpha: 0.15)) + emojiRegularStatusContent = .emojiStatus(status: emojiStatus, size: CGSize(width: 32.0, height: 32.0), placeholderColor: presentationData.theme.list.mediaPlaceholderColor) + emojiExpandedStatusContent = .emojiStatus(status: emojiStatus, size: CGSize(width: 32.0, height: 32.0), placeholderColor: UIColor(rgb: 0xffffff, alpha: 0.15)) } let iconSize = self.titleCredibilityIconView.update( @@ -2439,13 +2447,50 @@ final class PeerInfoHeaderNode: ASDisplayNode { guard let strongSelf = self else { return } - strongSelf.displayPremiumIntro?(strongSelf.titleCredibilityIconView, currentEmojiStatus, false) + strongSelf.displayPremiumIntro?(strongSelf.titleCredibilityIconView, currentEmojiStatus, strongSelf.emojiStatusFile, strongSelf.emojiStatusPackTitle, false) }, longTapAction: { [weak self] in guard let strongSelf = self else { return } let _ = strongSelf.context.engine.accountData.setEmojiStatus(file: nil).start() + }, emojiFileUpdated: { [weak self] emojiFile in + guard let strongSelf = self else { + return + } + + if let emojiFile = emojiFile { + strongSelf.emojiStatusFile = nil + for attribute in emojiFile.attributes { + if case let .CustomEmoji(_, _, packReference) = attribute, let packReference = packReference { + strongSelf.emojiStatusPackDisposable.set((strongSelf.context.engine.stickers.loadedStickerPack(reference: packReference, forceActualized: false) + |> filter { result in + if case .result = result { + return true + } else { + return false + } + } + |> mapToSignal { result -> Signal<(TelegramMediaFile, String)?, NoError> in + if case let .result(info, items, _) = result { + return .single(items.first.flatMap { ($0.file, info.title) }) + } else { + return .complete() + } + }).start(next: { fileAndPackTitle in + guard let strongSelf = self else { + return + } + strongSelf.emojiStatusFile = fileAndPackTitle?.0 + strongSelf.emojiStatusPackTitle = fileAndPackTitle?.1 + })) + break + } + } + } else { + strongSelf.emojiStatusFile = nil + strongSelf.emojiStatusPackDisposable.set(nil) + } } )), environment: {}, @@ -2462,7 +2507,7 @@ final class PeerInfoHeaderNode: ASDisplayNode { guard let strongSelf = self else { return } - strongSelf.displayPremiumIntro?(strongSelf.titleExpandedCredibilityIconView, currentEmojiStatus, true) + strongSelf.displayPremiumIntro?(strongSelf.titleExpandedCredibilityIconView, currentEmojiStatus, strongSelf.emojiStatusFile, strongSelf.emojiStatusPackTitle, true) }, longTapAction: { [weak self] in guard let strongSelf = self else { diff --git a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift index baa4a26e36..dcedc38208 100644 --- a/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift +++ b/submodules/TelegramUI/Sources/PeerInfo/PeerInfoScreen.swift @@ -3051,7 +3051,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate })) } - self.headerNode.displayPremiumIntro = { [weak self] sourceView, _, _ in + self.headerNode.displayPremiumIntro = { [weak self] sourceView, _, _, _, _ in guard let strongSelf = self else { return } @@ -3080,26 +3080,36 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate } else { screenData = peerInfoScreenData(context: context, peerId: peerId, strings: self.presentationData.strings, dateTimeFormat: self.presentationData.dateTimeFormat, isSettings: self.isSettings, hintGroupInCommon: hintGroupInCommon, existingRequestsContext: requestsContext) - self.headerNode.displayPremiumIntro = { [weak self] sourceView, peerStatus, white in + self.headerNode.displayPremiumIntro = { [weak self] sourceView, peerStatus, emojiStatusFile, emojiPackTitle, white in guard let strongSelf = self else { return } - let source: PremiumSource - if let peerStatus = peerStatus { - source = .emojiStatus(strongSelf.peerId, peerStatus.fileId) - } else { - source = .profile(strongSelf.peerId) + let premiumConfiguration = PremiumConfiguration.with(appConfiguration: strongSelf.context.currentAppConfiguration.with { $0 }) + guard !premiumConfiguration.isPremiumDisabled else { + return } - let premiumConfiguration = PremiumConfiguration.with(appConfiguration: strongSelf.context.currentAppConfiguration.with { $0 }) - if !premiumConfiguration.isPremiumDisabled { - let controller = PremiumIntroScreen(context: strongSelf.context, source: source) - controller.sourceView = sourceView - controller.containerView = strongSelf.controller?.navigationController?.view - controller.animationColor = white ? .white : strongSelf.presentationData.theme.list.itemAccentColor - strongSelf.controller?.push(controller) - } + let _ = (strongSelf.context.engine.data.get(TelegramEngine.EngineData.Item.Peer.Peer(id: strongSelf.context.account.peerId)) + |> deliverOnMainQueue).start(next: { [weak self] _ in + guard let strongSelf = self else { + return + } + if let emojiFile = emojiStatusFile { + let source: PremiumSource + if let peerStatus = peerStatus { + source = .emojiStatus(strongSelf.peerId, peerStatus.fileId, emojiStatusFile, emojiPackTitle) + } else { + source = .profile(strongSelf.peerId) + } + + let controller = PremiumIntroScreen(context: strongSelf.context, source: source) + controller.sourceView = sourceView + controller.containerView = strongSelf.controller?.navigationController?.view + controller.animationColor = white ? .white : strongSelf.presentationData.theme.list.itemAccentColor + strongSelf.controller?.push(controller) + } + }) } self.headerNode.displayAvatarContextMenu = { [weak self] node, gesture in diff --git a/submodules/TelegramUI/Sources/ReplyAccessoryPanelNode.swift b/submodules/TelegramUI/Sources/ReplyAccessoryPanelNode.swift index 4469e3bf1a..1590136365 100644 --- a/submodules/TelegramUI/Sources/ReplyAccessoryPanelNode.swift +++ b/submodules/TelegramUI/Sources/ReplyAccessoryPanelNode.swift @@ -345,7 +345,6 @@ final class ReplyAccessoryPanelNode: AccessoryPanelNode { let dustNode = InvisibleInkDustNode(textNode: nil) self.dustNode = dustNode self.textNode.supernode?.insertSubnode(dustNode, aboveSubnode: self.textNode) - } if let dustNode = self.dustNode { dustNode.update(size: textFrame.size, color: self.theme.chat.inputPanel.secondaryTextColor, textColor: self.theme.chat.inputPanel.primaryTextColor, rects: textLayout.spoilers.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) }, wordRects: textLayout.spoilerWords.map { $0.1.offsetBy(dx: 3.0, dy: 3.0).insetBy(dx: 1.0, dy: 1.0) }) diff --git a/submodules/TelegramUI/Sources/SharedAccountContext.swift b/submodules/TelegramUI/Sources/SharedAccountContext.swift index e9497c46e4..b5c891a183 100644 --- a/submodules/TelegramUI/Sources/SharedAccountContext.swift +++ b/submodules/TelegramUI/Sources/SharedAccountContext.swift @@ -1501,8 +1501,8 @@ public final class SharedAccountContextImpl: SharedAccountContext { mappedSource = .deeplink(reference) case let .profile(peerId): mappedSource = .profile(peerId) - case let .emojiStatus(peerId, fileId): - mappedSource = .emojiStatus(peerId, fileId) + case let .emojiStatus(peerId, fileId, file, packTitle): + mappedSource = .emojiStatus(peerId, fileId, file, packTitle) } return PremiumIntroScreen(context: context, source: mappedSource) }