mirror of
https://github.com/Swiftgram/Telegram-iOS.git
synced 2025-06-16 05:55:20 +00:00
Various improvements
This commit is contained in:
parent
7f2ab1788f
commit
bb06841df3
@ -7809,8 +7809,6 @@ Sorry for the inconvenience.";
|
|||||||
"Premium.Gift.Info" = "You can review the list of features and terms of use for Telegram Premium [here]().";
|
"Premium.Gift.Info" = "You can review the list of features and terms of use for Telegram Premium [here]().";
|
||||||
"Premium.Gift.GiftSubscription" = "Gift Subscription for %@";
|
"Premium.Gift.GiftSubscription" = "Gift Subscription for %@";
|
||||||
|
|
||||||
"Premium.Gift.PricePerMonth" = "%@ / month";
|
|
||||||
|
|
||||||
"Premium.Gift.Months_1" = "%@ Month";
|
"Premium.Gift.Months_1" = "%@ Month";
|
||||||
"Premium.Gift.Months_any" = "%@ Months";
|
"Premium.Gift.Months_any" = "%@ Months";
|
||||||
|
|
||||||
@ -7986,15 +7984,23 @@ Sorry for the inconvenience.";
|
|||||||
"Login.EnterCodeSMSTitle" = "Enter Code";
|
"Login.EnterCodeSMSTitle" = "Enter Code";
|
||||||
"Login.EnterCodeSMSText" = "We've sent and SMS with an activation code to your phone **%@**.";
|
"Login.EnterCodeSMSText" = "We've sent and SMS with an activation code to your phone **%@**.";
|
||||||
"Login.SendCodeAsSMS" = "Send the code as an SMS";
|
"Login.SendCodeAsSMS" = "Send the code as an SMS";
|
||||||
|
|
||||||
"Login.EnterCodeTelegramTitle" = "Enter Code";
|
"Login.EnterCodeTelegramTitle" = "Enter Code";
|
||||||
"Login.EnterCodeTelegramText" = "We've sent the code to the **Telegram app** for %@ on your other device.";
|
"Login.EnterCodeTelegramText" = "We've sent the code to the **Telegram app** for %@ on your other device.";
|
||||||
|
|
||||||
"Login.AddEmailTitle" = "Add Email";
|
"Login.AddEmailTitle" = "Add Email";
|
||||||
"Login.AddEmailText" = "Please enter your valid email address to protect your account.";
|
"Login.AddEmailText" = "Please enter your valid email address to protect your account.";
|
||||||
"Login.AddEmailPlaceholder" = "Enter your email";
|
"Login.AddEmailPlaceholder" = "Enter your email";
|
||||||
|
|
||||||
"Login.EnterCodeEmailTitle" = "Check Your Email";
|
"Login.EnterCodeEmailTitle" = "Check Your Email";
|
||||||
"Login.EnterCodeEmailText" = "Please enter the code we have sent to your email %@.";
|
"Login.EnterCodeEmailText" = "Please enter the code we have sent to your email %@.";
|
||||||
|
"Login.EnterNewEmailTitle" = "Enter New Email";
|
||||||
|
"Login.EnterCodeNewEmailTitle" = "Check Your New Email";
|
||||||
|
"Login.EnterCodeNewEmailText" = "Please enter the code we have sent to your new email %@.";
|
||||||
"Login.WrongCodeError" = "Wrong code, please try again.";
|
"Login.WrongCodeError" = "Wrong code, please try again.";
|
||||||
|
|
||||||
|
"PrivacySettings.LoginEmail" = "Login Email";
|
||||||
|
|
||||||
|
"Conversation.EmojiCopied" = "Emoji copied to clipboard";
|
||||||
|
|
||||||
|
"Conversation.EmojiTooltip" = "Tap here to choose more emoji";
|
||||||
|
|
||||||
|
"Premium.PricePerMonth" = "%@/month";
|
||||||
|
"Premium.PricePerYear" = "%@/year";
|
||||||
|
@ -23,6 +23,7 @@ swift_library(
|
|||||||
"//submodules/MeshAnimationCache:MeshAnimationCache",
|
"//submodules/MeshAnimationCache:MeshAnimationCache",
|
||||||
"//submodules/Utils/RangeSet:RangeSet",
|
"//submodules/Utils/RangeSet:RangeSet",
|
||||||
"//submodules/InAppPurchaseManager:InAppPurchaseManager",
|
"//submodules/InAppPurchaseManager:InAppPurchaseManager",
|
||||||
|
"//submodules/TextFormat:TextFormat",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -752,6 +752,8 @@ public protocol SharedAccountContext: AnyObject {
|
|||||||
|
|
||||||
func makeStickerPackScreen(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, mainStickerPack: StickerPackReference, stickerPacks: [StickerPackReference], parentNavigationController: NavigationController?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?) -> ViewController
|
func makeStickerPackScreen(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, mainStickerPack: StickerPackReference, stickerPacks: [StickerPackReference], parentNavigationController: NavigationController?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?) -> ViewController
|
||||||
|
|
||||||
|
func makeProxySettingsController(sharedContext: SharedAccountContext, account: UnauthorizedAccount) -> ViewController
|
||||||
|
|
||||||
func navigateToCurrentCall()
|
func navigateToCurrentCall()
|
||||||
var hasOngoingCall: ValuePromise<Bool> { get }
|
var hasOngoingCall: ValuePromise<Bool> { get }
|
||||||
var immediateHasOngoingCall: Bool { get }
|
var immediateHasOngoingCall: Bool { get }
|
||||||
|
@ -7,6 +7,7 @@ import Display
|
|||||||
import AsyncDisplayKit
|
import AsyncDisplayKit
|
||||||
import UniversalMediaPlayer
|
import UniversalMediaPlayer
|
||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
|
import TextFormat
|
||||||
|
|
||||||
public enum ChatControllerInteractionOpenMessageMode {
|
public enum ChatControllerInteractionOpenMessageMode {
|
||||||
case `default`
|
case `default`
|
||||||
@ -37,6 +38,7 @@ public final class OpenChatMessageParams {
|
|||||||
public let callPeer: (PeerId, Bool) -> Void
|
public let callPeer: (PeerId, Bool) -> Void
|
||||||
public let enqueueMessage: (EnqueueMessage) -> Void
|
public let enqueueMessage: (EnqueueMessage) -> Void
|
||||||
public let sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?
|
public let sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?
|
||||||
|
public let sendEmoji: ((String, ChatTextInputTextCustomEmojiAttribute) -> Void)?
|
||||||
public let setupTemporaryHiddenMedia: (Signal<Any?, NoError>, Int, Media) -> Void
|
public let setupTemporaryHiddenMedia: (Signal<Any?, NoError>, Int, Media) -> Void
|
||||||
public let chatAvatarHiddenMedia: (Signal<MessageId?, NoError>, Media) -> Void
|
public let chatAvatarHiddenMedia: (Signal<MessageId?, NoError>, Media) -> Void
|
||||||
public let actionInteraction: GalleryControllerActionInteraction?
|
public let actionInteraction: GalleryControllerActionInteraction?
|
||||||
@ -64,6 +66,7 @@ public final class OpenChatMessageParams {
|
|||||||
callPeer: @escaping (PeerId, Bool) -> Void,
|
callPeer: @escaping (PeerId, Bool) -> Void,
|
||||||
enqueueMessage: @escaping (EnqueueMessage) -> Void,
|
enqueueMessage: @escaping (EnqueueMessage) -> Void,
|
||||||
sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?,
|
sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?,
|
||||||
|
sendEmoji: ((String, ChatTextInputTextCustomEmojiAttribute) -> Void)?,
|
||||||
setupTemporaryHiddenMedia: @escaping (Signal<Any?, NoError>, Int, Media) -> Void,
|
setupTemporaryHiddenMedia: @escaping (Signal<Any?, NoError>, Int, Media) -> Void,
|
||||||
chatAvatarHiddenMedia: @escaping (Signal<MessageId?, NoError>, Media) -> Void,
|
chatAvatarHiddenMedia: @escaping (Signal<MessageId?, NoError>, Media) -> Void,
|
||||||
actionInteraction: GalleryControllerActionInteraction? = nil,
|
actionInteraction: GalleryControllerActionInteraction? = nil,
|
||||||
@ -90,6 +93,7 @@ public final class OpenChatMessageParams {
|
|||||||
self.callPeer = callPeer
|
self.callPeer = callPeer
|
||||||
self.enqueueMessage = enqueueMessage
|
self.enqueueMessage = enqueueMessage
|
||||||
self.sendSticker = sendSticker
|
self.sendSticker = sendSticker
|
||||||
|
self.sendEmoji = sendEmoji
|
||||||
self.setupTemporaryHiddenMedia = setupTemporaryHiddenMedia
|
self.setupTemporaryHiddenMedia = setupTemporaryHiddenMedia
|
||||||
self.chatAvatarHiddenMedia = chatAvatarHiddenMedia
|
self.chatAvatarHiddenMedia = chatAvatarHiddenMedia
|
||||||
self.actionInteraction = actionInteraction
|
self.actionInteraction = actionInteraction
|
||||||
|
@ -12,10 +12,32 @@ swift_library(
|
|||||||
deps = [
|
deps = [
|
||||||
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
|
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
|
||||||
"//submodules/TelegramCore:TelegramCore",
|
"//submodules/TelegramCore:TelegramCore",
|
||||||
|
"//submodules/Postbox:Postbox",
|
||||||
"//submodules/Display:Display",
|
"//submodules/Display:Display",
|
||||||
|
"//submodules/SSignalKit/SwiftSignalKit:SwiftSignalKit",
|
||||||
"//submodules/TextFormat:TextFormat",
|
"//submodules/TextFormat:TextFormat",
|
||||||
"//submodules/Markdown:Markdown",
|
"//submodules/Markdown:Markdown",
|
||||||
"//submodules/TelegramPresentationData:TelegramPresentationData",
|
"//submodules/TelegramPresentationData:TelegramPresentationData",
|
||||||
|
"//submodules/AccountContext:AccountContext",
|
||||||
|
"//submodules/CountrySelectionUI:CountrySelectionUI",
|
||||||
|
"//submodules/PhoneNumberFormat:PhoneNumberFormat",
|
||||||
|
"//submodules/LegacyComponents:LegacyComponents",
|
||||||
|
"//submodules/LegacyMediaPickerUI:LegacyMediaPickerUI",
|
||||||
|
"//submodules/PasswordSetupUI:PasswordSetupUI",
|
||||||
|
"//submodules/TelegramNotices:TelegramNotices",
|
||||||
|
"//submodules/ProgressNavigationButtonNode:ProgressNavigationButtonNode",
|
||||||
|
"//submodules/AnimatedStickerNode:AnimatedStickerNode",
|
||||||
|
"//submodules/TelegramAnimatedStickerNode:TelegramAnimatedStickerNode",
|
||||||
|
"//submodules/SolidRoundedButtonNode:SolidRoundedButtonNode",
|
||||||
|
"//submodules/ImageCompression:ImageCompression",
|
||||||
|
"//submodules/RMIntro:RMIntro",
|
||||||
|
"//submodules/QrCode:QrCode",
|
||||||
|
"//submodules/PhoneInputNode:PhoneInputNode",
|
||||||
|
"//submodules/CodeInputView:CodeInputView",
|
||||||
|
"//submodules/DebugSettingsUI:DebugSettingsUI",
|
||||||
|
"//submodules/InvisibleInkDustNode:InvisibleInkDustNode",
|
||||||
|
"//submodules/AuthorizationUtils:AuthorizationUtils",
|
||||||
|
"//submodules/ManagedAnimationNode:ManagedAnimationNode",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -4,7 +4,7 @@ import AsyncDisplayKit
|
|||||||
import Display
|
import Display
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
import AuthorizationUI
|
import AuthorizationUtils
|
||||||
|
|
||||||
private func timerValueString(days: Int32, hours: Int32, minutes: Int32, color: UIColor, strings: PresentationStrings) -> NSAttributedString {
|
private func timerValueString(days: Int32, hours: Int32, minutes: Int32, color: UIColor, strings: PresentationStrings) -> NSAttributedString {
|
||||||
var daysString = ""
|
var daysString = ""
|
@ -6,7 +6,7 @@ import TelegramCore
|
|||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
import ProgressNavigationButtonNode
|
import ProgressNavigationButtonNode
|
||||||
|
|
||||||
final class AuthorizationSequenceCodeEntryController: ViewController {
|
public final class AuthorizationSequenceCodeEntryController: ViewController {
|
||||||
private var controllerNode: AuthorizationSequenceCodeEntryControllerNode {
|
private var controllerNode: AuthorizationSequenceCodeEntryControllerNode {
|
||||||
return self.displayNode as! AuthorizationSequenceCodeEntryControllerNode
|
return self.displayNode as! AuthorizationSequenceCodeEntryControllerNode
|
||||||
}
|
}
|
||||||
@ -15,20 +15,20 @@ final class AuthorizationSequenceCodeEntryController: ViewController {
|
|||||||
private let theme: PresentationTheme
|
private let theme: PresentationTheme
|
||||||
private let openUrl: (String) -> Void
|
private let openUrl: (String) -> Void
|
||||||
|
|
||||||
var loginWithCode: ((String) -> Void)?
|
public var loginWithCode: ((String) -> Void)?
|
||||||
var signInWithApple: (() -> Void)?
|
public var signInWithApple: (() -> Void)?
|
||||||
|
|
||||||
var reset: (() -> Void)?
|
var reset: (() -> Void)?
|
||||||
var requestNextOption: (() -> Void)?
|
var requestNextOption: (() -> Void)?
|
||||||
|
|
||||||
var data: (String, SentAuthorizationCodeType, AuthorizationCodeNextType?, Int32?)?
|
var data: (String, String?, SentAuthorizationCodeType, AuthorizationCodeNextType?, Int32?)?
|
||||||
var termsOfService: (UnauthorizedAccountTermsOfService, Bool)?
|
var termsOfService: (UnauthorizedAccountTermsOfService, Bool)?
|
||||||
|
|
||||||
private let hapticFeedback = HapticFeedback()
|
private let hapticFeedback = HapticFeedback()
|
||||||
|
|
||||||
private var appleSignInAllowed = false
|
private var appleSignInAllowed = false
|
||||||
|
|
||||||
var inProgress: Bool = false {
|
public var inProgress: Bool = false {
|
||||||
didSet {
|
didSet {
|
||||||
// if self.inProgress {
|
// if self.inProgress {
|
||||||
// let item = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(color: self.theme.rootController.navigationBar.accentTextColor))
|
// let item = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(color: self.theme.rootController.navigationBar.accentTextColor))
|
||||||
@ -40,7 +40,7 @@ final class AuthorizationSequenceCodeEntryController: ViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init(presentationData: PresentationData, openUrl: @escaping (String) -> Void, back: @escaping () -> Void) {
|
public init(presentationData: PresentationData, openUrl: @escaping (String) -> Void, back: @escaping () -> Void) {
|
||||||
self.strings = presentationData.strings
|
self.strings = presentationData.strings
|
||||||
self.theme = presentationData.theme
|
self.theme = presentationData.theme
|
||||||
self.openUrl = openUrl
|
self.openUrl = openUrl
|
||||||
@ -96,16 +96,16 @@ final class AuthorizationSequenceCodeEntryController: ViewController {
|
|||||||
self?.navigationItem.rightBarButtonItem?.isEnabled = value
|
self?.navigationItem.rightBarButtonItem?.isEnabled = value
|
||||||
}
|
}
|
||||||
|
|
||||||
if let (number, codeType, nextType, timeout) = self.data {
|
if let (number, email, codeType, nextType, timeout) = self.data {
|
||||||
var appleSignInAllowed = false
|
var appleSignInAllowed = false
|
||||||
if case let .email(_, _, _, appleSignInAllowedValue, _) = codeType {
|
if case let .email(_, _, _, appleSignInAllowedValue, _) = codeType {
|
||||||
appleSignInAllowed = appleSignInAllowedValue
|
appleSignInAllowed = appleSignInAllowedValue
|
||||||
}
|
}
|
||||||
self.controllerNode.updateData(number: number, codeType: codeType, nextType: nextType, timeout: timeout, appleSignInAllowed: appleSignInAllowed)
|
self.controllerNode.updateData(number: number, email: email, codeType: codeType, nextType: nextType, timeout: timeout, appleSignInAllowed: appleSignInAllowed)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewDidAppear(_ animated: Bool) {
|
override public func viewDidAppear(_ animated: Bool) {
|
||||||
super.viewDidAppear(animated)
|
super.viewDidAppear(animated)
|
||||||
|
|
||||||
self.controllerNode.activateInput()
|
self.controllerNode.activateInput()
|
||||||
@ -115,19 +115,19 @@ final class AuthorizationSequenceCodeEntryController: ViewController {
|
|||||||
self.controllerNode.resetCode()
|
self.controllerNode.resetCode()
|
||||||
}
|
}
|
||||||
|
|
||||||
func animateSuccess() {
|
public func animateSuccess() {
|
||||||
self.controllerNode.animateSuccess()
|
self.controllerNode.animateSuccess()
|
||||||
}
|
}
|
||||||
|
|
||||||
func animateError(text: String) {
|
public func animateError(text: String) {
|
||||||
self.hapticFeedback.error()
|
self.hapticFeedback.error()
|
||||||
self.controllerNode.animateError(text: text)
|
self.controllerNode.animateError(text: text)
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateData(number: String, codeType: SentAuthorizationCodeType, nextType: AuthorizationCodeNextType?, timeout: Int32?, termsOfService: (UnauthorizedAccountTermsOfService, Bool)?) {
|
public func updateData(number: String, email: String?, codeType: SentAuthorizationCodeType, nextType: AuthorizationCodeNextType?, timeout: Int32?, termsOfService: (UnauthorizedAccountTermsOfService, Bool)?) {
|
||||||
self.termsOfService = termsOfService
|
self.termsOfService = termsOfService
|
||||||
if self.data?.0 != number || self.data?.1 != codeType || self.data?.2 != nextType || self.data?.3 != timeout {
|
if self.data?.0 != number || self.data?.1 != email || self.data?.2 != codeType || self.data?.3 != nextType || self.data?.4 != timeout {
|
||||||
self.data = (number, codeType, nextType, timeout)
|
self.data = (number, email, codeType, nextType, timeout)
|
||||||
|
|
||||||
var appleSignInAllowed = false
|
var appleSignInAllowed = false
|
||||||
if case let .email(_, _, _, appleSignInAllowedValue, _) = codeType {
|
if case let .email(_, _, _, appleSignInAllowedValue, _) = codeType {
|
||||||
@ -135,20 +135,20 @@ final class AuthorizationSequenceCodeEntryController: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if self.isNodeLoaded {
|
if self.isNodeLoaded {
|
||||||
self.controllerNode.updateData(number: number, codeType: codeType, nextType: nextType, timeout: timeout, appleSignInAllowed: appleSignInAllowed)
|
self.controllerNode.updateData(number: number, email: email, codeType: codeType, nextType: nextType, timeout: timeout, appleSignInAllowed: appleSignInAllowed)
|
||||||
self.requestLayout(transition: .immediate)
|
self.requestLayout(transition: .immediate)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||||
super.containerLayoutUpdated(layout, transition: transition)
|
super.containerLayoutUpdated(layout, transition: transition)
|
||||||
|
|
||||||
self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
|
self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func nextPressed() {
|
@objc private func nextPressed() {
|
||||||
guard let (_, type, _, _) = self.data else {
|
guard let (_, _, type, _, _) = self.data else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
@ -6,7 +6,6 @@ import TelegramCore
|
|||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
import TextFormat
|
import TextFormat
|
||||||
import AuthorizationUI
|
|
||||||
import AuthenticationServices
|
import AuthenticationServices
|
||||||
import CodeInputView
|
import CodeInputView
|
||||||
import PhoneNumberFormat
|
import PhoneNumberFormat
|
||||||
@ -14,6 +13,7 @@ import AnimatedStickerNode
|
|||||||
import TelegramAnimatedStickerNode
|
import TelegramAnimatedStickerNode
|
||||||
import SolidRoundedButtonNode
|
import SolidRoundedButtonNode
|
||||||
import InvisibleInkDustNode
|
import InvisibleInkDustNode
|
||||||
|
import AuthorizationUtils
|
||||||
|
|
||||||
final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextFieldDelegate {
|
final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextFieldDelegate {
|
||||||
private let strings: PresentationStrings
|
private let strings: PresentationStrings
|
||||||
@ -54,6 +54,8 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var email: String?
|
||||||
|
|
||||||
var currentCode: String {
|
var currentCode: String {
|
||||||
return self.codeInputView.text
|
return self.codeInputView.text
|
||||||
}
|
}
|
||||||
@ -202,9 +204,10 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
|
|||||||
self.codeInputView.text = ""
|
self.codeInputView.text = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateData(number: String, codeType: SentAuthorizationCodeType, nextType: AuthorizationCodeNextType?, timeout: Int32?, appleSignInAllowed: Bool) {
|
func updateData(number: String, email: String?, codeType: SentAuthorizationCodeType, nextType: AuthorizationCodeNextType?, timeout: Int32?, appleSignInAllowed: Bool) {
|
||||||
self.codeType = codeType
|
self.codeType = codeType
|
||||||
self.phoneNumber = number
|
self.phoneNumber = number
|
||||||
|
self.email = email
|
||||||
|
|
||||||
var appleSignInAllowed = appleSignInAllowed
|
var appleSignInAllowed = appleSignInAllowed
|
||||||
if #available(iOS 13.0, *) {
|
if #available(iOS 13.0, *) {
|
||||||
@ -213,7 +216,7 @@ final class AuthorizationSequenceCodeEntryControllerNode: ASDisplayNode, UITextF
|
|||||||
}
|
}
|
||||||
self.appleSignInAllowed = appleSignInAllowed
|
self.appleSignInAllowed = appleSignInAllowed
|
||||||
|
|
||||||
self.currentOptionNode.attributedText = authorizationCurrentOptionText(codeType, phoneNumber: self.phoneNumber, email: nil, strings: self.strings, primaryColor: self.theme.list.itemPrimaryTextColor, accentColor: self.theme.list.itemAccentColor)
|
self.currentOptionNode.attributedText = authorizationCurrentOptionText(codeType, phoneNumber: self.phoneNumber, email: self.email, strings: self.strings, primaryColor: self.theme.list.itemPrimaryTextColor, accentColor: self.theme.list.itemAccentColor)
|
||||||
if case .missedCall = codeType {
|
if case .missedCall = codeType {
|
||||||
self.currentOptionInfoNode.attributedText = NSAttributedString(string: self.strings.Login_CodePhonePatternInfoText, font: Font.regular(17.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 {
|
} else {
|
@ -12,7 +12,6 @@ import TelegramPresentationData
|
|||||||
import TextFormat
|
import TextFormat
|
||||||
import AccountContext
|
import AccountContext
|
||||||
import CountrySelectionUI
|
import CountrySelectionUI
|
||||||
import SettingsUI
|
|
||||||
import PhoneNumberFormat
|
import PhoneNumberFormat
|
||||||
import LegacyComponents
|
import LegacyComponents
|
||||||
import LegacyMediaPickerUI
|
import LegacyMediaPickerUI
|
||||||
@ -247,7 +246,7 @@ public final class AuthorizationSequenceController: NavigationController, MFMail
|
|||||||
guard let strongSelf = self, let controller = controller else {
|
guard let strongSelf = self, let controller = controller else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
controller.present(proxySettingsController(accountManager: strongSelf.sharedContext.accountManager, postbox: strongSelf.account.postbox, network: strongSelf.account.network, mode: .modal, presentationData: strongSelf.sharedContext.currentPresentationData.with { $0 }, updatedPresentationData: strongSelf.sharedContext.presentationData), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
controller.present(strongSelf.sharedContext.makeProxySettingsController(sharedContext: strongSelf.sharedContext, account: strongSelf.account), in: .window(.root), with: ViewControllerPresentationArguments(presentationAnimation: .modalSheet))
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: actions), in: .window(.root))
|
controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: actions), in: .window(.root))
|
||||||
@ -260,11 +259,11 @@ public final class AuthorizationSequenceController: NavigationController, MFMail
|
|||||||
return controller
|
return controller
|
||||||
}
|
}
|
||||||
|
|
||||||
private func codeEntryController(number: String, type: SentAuthorizationCodeType, nextType: AuthorizationCodeNextType?, timeout: Int32?, termsOfService: (UnauthorizedAccountTermsOfService, Bool)?) -> AuthorizationSequenceCodeEntryController {
|
private func codeEntryController(number: String, email: String?, type: SentAuthorizationCodeType, nextType: AuthorizationCodeNextType?, timeout: Int32?, termsOfService: (UnauthorizedAccountTermsOfService, Bool)?) -> AuthorizationSequenceCodeEntryController {
|
||||||
var currentController: AuthorizationSequenceCodeEntryController?
|
var currentController: AuthorizationSequenceCodeEntryController?
|
||||||
for c in self.viewControllers {
|
for c in self.viewControllers {
|
||||||
if let c = c as? AuthorizationSequenceCodeEntryController {
|
if let c = c as? AuthorizationSequenceCodeEntryController {
|
||||||
if c.data?.1 == type {
|
if c.data?.2 == type {
|
||||||
currentController = c
|
currentController = c
|
||||||
}
|
}
|
||||||
break
|
break
|
||||||
@ -502,11 +501,14 @@ public final class AuthorizationSequenceController: NavigationController, MFMail
|
|||||||
authorizationController.performRequests()
|
authorizationController.performRequests()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
controller.updateData(number: formatPhoneNumber(number), codeType: type, nextType: nextType, timeout: timeout, termsOfService: termsOfService)
|
controller.updateData(number: formatPhoneNumber(number), email: email, codeType: type, nextType: nextType, timeout: timeout, termsOfService: termsOfService)
|
||||||
return controller
|
return controller
|
||||||
}
|
}
|
||||||
|
|
||||||
private var signInWithAppleSetup = false
|
private var signInWithAppleSetup = false
|
||||||
|
private var appleSignInAllowed = false
|
||||||
|
private var currentEmail: String?
|
||||||
|
|
||||||
private func emailSetupController(number: String, appleSignInAllowed: Bool) -> AuthorizationSequenceEmailEntryController {
|
private func emailSetupController(number: String, appleSignInAllowed: Bool) -> AuthorizationSequenceEmailEntryController {
|
||||||
var currentController: AuthorizationSequenceEmailEntryController?
|
var currentController: AuthorizationSequenceEmailEntryController?
|
||||||
for c in self.viewControllers {
|
for c in self.viewControllers {
|
||||||
@ -519,7 +521,7 @@ public final class AuthorizationSequenceController: NavigationController, MFMail
|
|||||||
if let currentController = currentController {
|
if let currentController = currentController {
|
||||||
controller = currentController
|
controller = currentController
|
||||||
} else {
|
} else {
|
||||||
controller = AuthorizationSequenceEmailEntryController(presentationData: self.presentationData, back: { [weak self] in
|
controller = AuthorizationSequenceEmailEntryController(presentationData: self.presentationData, mode: .setup, back: { [weak self] in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -536,9 +538,7 @@ public final class AuthorizationSequenceController: NavigationController, MFMail
|
|||||||
controller?.inProgress = true
|
controller?.inProgress = true
|
||||||
|
|
||||||
strongSelf.actionDisposable.set((sendLoginEmailCode(account: strongSelf.account, email: email)
|
strongSelf.actionDisposable.set((sendLoginEmailCode(account: strongSelf.account, email: email)
|
||||||
|> deliverOnMainQueue).start(next: { result in
|
|> deliverOnMainQueue).start(error: { error in
|
||||||
controller?.inProgress = false
|
|
||||||
}, error: { error in
|
|
||||||
if let strongSelf = self, let controller = controller {
|
if let strongSelf = self, let controller = controller {
|
||||||
controller.inProgress = false
|
controller.inProgress = false
|
||||||
|
|
||||||
@ -554,6 +554,12 @@ public final class AuthorizationSequenceController: NavigationController, MFMail
|
|||||||
|
|
||||||
controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
controller.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: strongSelf.presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: strongSelf.presentationData.strings.Common_OK, action: {})]), in: .window(.root))
|
||||||
}
|
}
|
||||||
|
}, completed: { [weak self] in
|
||||||
|
controller?.inProgress = false
|
||||||
|
|
||||||
|
if let strongSelf = self {
|
||||||
|
strongSelf.currentEmail = email
|
||||||
|
}
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
controller.signInWithApple = { [weak self] in
|
controller.signInWithApple = { [weak self] in
|
||||||
@ -1017,9 +1023,13 @@ public final class AuthorizationSequenceController: NavigationController, MFMail
|
|||||||
}
|
}
|
||||||
controllers.append(self.phoneEntryController(countryCode: defaultCountryCode(), number: "", splashController: nil))
|
controllers.append(self.phoneEntryController(countryCode: defaultCountryCode(), number: "", splashController: nil))
|
||||||
if case let .emailSetupRequired(appleSignInAllowed) = type {
|
if case let .emailSetupRequired(appleSignInAllowed) = type {
|
||||||
|
self.appleSignInAllowed = appleSignInAllowed
|
||||||
controllers.append(self.emailSetupController(number: number, appleSignInAllowed: appleSignInAllowed))
|
controllers.append(self.emailSetupController(number: number, appleSignInAllowed: appleSignInAllowed))
|
||||||
} else {
|
} else {
|
||||||
controllers.append(self.codeEntryController(number: number, type: type, nextType: nextType, timeout: timeout, termsOfService: nil))
|
if let _ = self.currentEmail {
|
||||||
|
controllers.append(self.emailSetupController(number: number, appleSignInAllowed: self.appleSignInAllowed))
|
||||||
|
}
|
||||||
|
controllers.append(self.codeEntryController(number: number, email: self.currentEmail, type: type, nextType: nextType, timeout: timeout, termsOfService: nil))
|
||||||
}
|
}
|
||||||
self.setViewControllers(controllers, animated: !self.viewControllers.isEmpty)
|
self.setViewControllers(controllers, animated: !self.viewControllers.isEmpty)
|
||||||
case let .passwordEntry(hint, _, _, suggestReset, syncContacts):
|
case let .passwordEntry(hint, _, _, suggestReset, syncContacts):
|
@ -5,34 +5,42 @@ import AsyncDisplayKit
|
|||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
import ProgressNavigationButtonNode
|
import ProgressNavigationButtonNode
|
||||||
|
|
||||||
final class AuthorizationSequenceEmailEntryController: ViewController {
|
public final class AuthorizationSequenceEmailEntryController: ViewController {
|
||||||
|
public enum Mode {
|
||||||
|
case setup
|
||||||
|
case change
|
||||||
|
}
|
||||||
|
|
||||||
|
private let mode: Mode
|
||||||
|
|
||||||
private var controllerNode: AuthorizationSequenceEmailEntryControllerNode {
|
private var controllerNode: AuthorizationSequenceEmailEntryControllerNode {
|
||||||
return self.displayNode as! AuthorizationSequenceEmailEntryControllerNode
|
return self.displayNode as! AuthorizationSequenceEmailEntryControllerNode
|
||||||
}
|
}
|
||||||
|
|
||||||
private let presentationData: PresentationData
|
private let presentationData: PresentationData
|
||||||
|
|
||||||
var proceedWithEmail: ((String) -> Void)?
|
public var proceedWithEmail: ((String) -> Void)?
|
||||||
var signInWithApple: (() -> Void)?
|
public var signInWithApple: (() -> Void)?
|
||||||
|
|
||||||
private let hapticFeedback = HapticFeedback()
|
private let hapticFeedback = HapticFeedback()
|
||||||
|
|
||||||
private var appleSignInAllowed = false
|
private var appleSignInAllowed = false
|
||||||
|
|
||||||
var inProgress: Bool = false {
|
public var inProgress: Bool = false {
|
||||||
didSet {
|
didSet {
|
||||||
if self.inProgress {
|
// if self.inProgress {
|
||||||
let item = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(color: self.presentationData.theme.rootController.navigationBar.accentTextColor))
|
// let item = UIBarButtonItem(customDisplayNode: ProgressNavigationButtonNode(color: self.presentationData.theme.rootController.navigationBar.accentTextColor))
|
||||||
self.navigationItem.rightBarButtonItem = item
|
// self.navigationItem.rightBarButtonItem = item
|
||||||
} else {
|
// } else {
|
||||||
self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Next, style: .done, target: self, action: #selector(self.nextPressed))
|
// self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Next, style: .done, target: self, action: #selector(self.nextPressed))
|
||||||
}
|
// }
|
||||||
self.controllerNode.inProgress = self.inProgress
|
self.controllerNode.inProgress = self.inProgress
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init(presentationData: PresentationData, back: @escaping () -> Void) {
|
public init(presentationData: PresentationData, mode: Mode, back: @escaping () -> Void) {
|
||||||
self.presentationData = presentationData
|
self.presentationData = presentationData
|
||||||
|
self.mode = mode
|
||||||
|
|
||||||
super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: AuthorizationSequenceController.navigationBarTheme(presentationData.theme), strings: NavigationBarStrings(presentationStrings: presentationData.strings)))
|
super.init(navigationBarPresentationData: NavigationBarPresentationData(theme: AuthorizationSequenceController.navigationBarTheme(presentationData.theme), strings: NavigationBarStrings(presentationStrings: presentationData.strings)))
|
||||||
|
|
||||||
@ -57,7 +65,7 @@ final class AuthorizationSequenceEmailEntryController: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override public func loadDisplayNode() {
|
override public func loadDisplayNode() {
|
||||||
self.displayNode = AuthorizationSequenceEmailEntryControllerNode(strings: self.presentationData.strings, theme: self.presentationData.theme)
|
self.displayNode = AuthorizationSequenceEmailEntryControllerNode(strings: self.presentationData.strings, theme: self.presentationData.theme, mode: self.mode)
|
||||||
self.displayNodeDidLoad()
|
self.displayNodeDidLoad()
|
||||||
|
|
||||||
self.controllerNode.view.disableAutomaticKeyboardHandling = [.forward, .backward]
|
self.controllerNode.view.disableAutomaticKeyboardHandling = [.forward, .backward]
|
||||||
@ -73,13 +81,13 @@ final class AuthorizationSequenceEmailEntryController: ViewController {
|
|||||||
self.controllerNode.updateData(appleSignInAllowed: self.appleSignInAllowed)
|
self.controllerNode.updateData(appleSignInAllowed: self.appleSignInAllowed)
|
||||||
}
|
}
|
||||||
|
|
||||||
override func viewDidAppear(_ animated: Bool) {
|
override public func viewDidAppear(_ animated: Bool) {
|
||||||
super.viewDidAppear(animated)
|
super.viewDidAppear(animated)
|
||||||
|
|
||||||
self.controllerNode.activateInput()
|
self.controllerNode.activateInput()
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateData(appleSignInAllowed: Bool) {
|
public func updateData(appleSignInAllowed: Bool) {
|
||||||
var appleSignInAllowed = appleSignInAllowed
|
var appleSignInAllowed = appleSignInAllowed
|
||||||
if #available(iOS 13.0, *) {
|
if #available(iOS 13.0, *) {
|
||||||
} else {
|
} else {
|
||||||
@ -93,13 +101,13 @@ final class AuthorizationSequenceEmailEntryController: ViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||||
super.containerLayoutUpdated(layout, transition: transition)
|
super.containerLayoutUpdated(layout, transition: transition)
|
||||||
|
|
||||||
self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
|
self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func nextPressed() {
|
@objc private func nextPressed() {
|
||||||
if self.controllerNode.currentEmail.isEmpty {
|
if self.controllerNode.currentEmail.isEmpty {
|
||||||
if self.appleSignInAllowed {
|
if self.appleSignInAllowed {
|
||||||
self.signInWithApple?()
|
self.signInWithApple?()
|
@ -3,7 +3,7 @@ import UIKit
|
|||||||
import AsyncDisplayKit
|
import AsyncDisplayKit
|
||||||
import Display
|
import Display
|
||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
import AuthorizationUI
|
import AuthorizationUtils
|
||||||
import AuthenticationServices
|
import AuthenticationServices
|
||||||
import AnimatedStickerNode
|
import AnimatedStickerNode
|
||||||
import TelegramAnimatedStickerNode
|
import TelegramAnimatedStickerNode
|
||||||
@ -50,6 +50,7 @@ final class AuthorizationDividerNode: ASDisplayNode {
|
|||||||
final class AuthorizationSequenceEmailEntryControllerNode: ASDisplayNode, UITextFieldDelegate {
|
final class AuthorizationSequenceEmailEntryControllerNode: ASDisplayNode, UITextFieldDelegate {
|
||||||
private let strings: PresentationStrings
|
private let strings: PresentationStrings
|
||||||
private let theme: PresentationTheme
|
private let theme: PresentationTheme
|
||||||
|
private let mode: AuthorizationSequenceEmailEntryController.Mode
|
||||||
|
|
||||||
private let animationNode: AnimatedStickerNode
|
private let animationNode: AnimatedStickerNode
|
||||||
private let titleNode: ASTextNode
|
private let titleNode: ASTextNode
|
||||||
@ -79,9 +80,10 @@ final class AuthorizationSequenceEmailEntryControllerNode: ASDisplayNode, UIText
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
init(strings: PresentationStrings, theme: PresentationTheme) {
|
init(strings: PresentationStrings, theme: PresentationTheme, mode: AuthorizationSequenceEmailEntryController.Mode) {
|
||||||
self.strings = strings
|
self.strings = strings
|
||||||
self.theme = theme
|
self.theme = theme
|
||||||
|
self.mode = mode
|
||||||
|
|
||||||
self.animationNode = DefaultAnimatedStickerNodeImpl()
|
self.animationNode = DefaultAnimatedStickerNodeImpl()
|
||||||
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "IntroMail"), width: 256, height: 256, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
|
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "IntroMail"), width: 256, height: 256, playbackMode: .once, mode: .direct(cachePathPrefix: nil))
|
||||||
@ -90,7 +92,6 @@ final class AuthorizationSequenceEmailEntryControllerNode: ASDisplayNode, UIText
|
|||||||
self.titleNode = ASTextNode()
|
self.titleNode = ASTextNode()
|
||||||
self.titleNode.isUserInteractionEnabled = false
|
self.titleNode.isUserInteractionEnabled = false
|
||||||
self.titleNode.displaysAsynchronously = false
|
self.titleNode.displaysAsynchronously = false
|
||||||
self.titleNode.attributedText = NSAttributedString(string: self.strings.Login_AddEmailTitle, font: Font.light(30.0), textColor: self.theme.list.itemPrimaryTextColor)
|
|
||||||
|
|
||||||
self.noticeNode = ASTextNode()
|
self.noticeNode = ASTextNode()
|
||||||
self.noticeNode.isUserInteractionEnabled = false
|
self.noticeNode.isUserInteractionEnabled = false
|
||||||
@ -190,10 +191,10 @@ final class AuthorizationSequenceEmailEntryControllerNode: ASDisplayNode, UIText
|
|||||||
insets.top = layout.statusBarHeight ?? 20.0
|
insets.top = layout.statusBarHeight ?? 20.0
|
||||||
|
|
||||||
if let inputHeight = layout.inputHeight {
|
if let inputHeight = layout.inputHeight {
|
||||||
insets.bottom += max(inputHeight, insets.bottom)
|
insets.bottom = max(inputHeight, insets.bottom)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.titleNode.attributedText = NSAttributedString(string: self.strings.Login_AddEmailTitle, font: Font.bold(28.0), textColor: self.theme.list.itemPrimaryTextColor)
|
self.titleNode.attributedText = NSAttributedString(string: self.mode == .setup ? self.strings.Login_AddEmailTitle : self.strings.Login_EnterNewEmailTitle, font: Font.bold(28.0), textColor: self.theme.list.itemPrimaryTextColor)
|
||||||
|
|
||||||
let animationSize = CGSize(width: 100.0, height: 100.0)
|
let animationSize = CGSize(width: 100.0, height: 100.0)
|
||||||
let titleSize = self.titleNode.measure(CGSize(width: layout.size.width, height: CGFloat.greatestFiniteMagnitude))
|
let titleSize = self.titleNode.measure(CGSize(width: layout.size.width, height: CGFloat.greatestFiniteMagnitude))
|
||||||
@ -209,19 +210,22 @@ final class AuthorizationSequenceEmailEntryControllerNode: ASDisplayNode, UIText
|
|||||||
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.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: 18.0, maxValue: 18.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.codeField, size: CGSize(width: layout.size.width - 88.0, height: 44.0), spacingBefore: AuthorizationLayoutItemSpacing(weight: 22.0, maxValue: 40.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)))
|
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)))
|
||||||
|
|
||||||
|
let inset: CGFloat = 24.0
|
||||||
|
let buttonFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - proceedSize.width) / 2.0), y: layout.size.height - insets.bottom - proceedSize.height - inset), size: proceedSize)
|
||||||
|
transition.updateFrame(node: self.proceedNode, frame: buttonFrame)
|
||||||
|
|
||||||
|
let dividerSize = self.dividerNode.updateLayout(width: layout.size.width)
|
||||||
|
transition.updateFrame(node: self.dividerNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - dividerSize.width) / 2.0), y: buttonFrame.minY - dividerSize.height), size: dividerSize))
|
||||||
|
|
||||||
if let _ = self.signInWithAppleButton, self.appleSignInAllowed {
|
if let _ = self.signInWithAppleButton, self.appleSignInAllowed {
|
||||||
self.dividerNode.isHidden = false
|
self.dividerNode.isHidden = false
|
||||||
let dividerSize = self.dividerNode.updateLayout(width: layout.size.width)
|
|
||||||
items.append(AuthorizationLayoutItem(node: self.dividerNode, size: dividerSize, spacingBefore: AuthorizationLayoutItemSpacing(weight: 48.0, maxValue: 100.0), spacingAfter: AuthorizationLayoutItemSpacing(weight: 0.0, maxValue: 0.0)))
|
|
||||||
} else {
|
} else {
|
||||||
self.dividerNode.isHidden = true
|
self.dividerNode.isHidden = true
|
||||||
}
|
}
|
||||||
|
|
||||||
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 - 80.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 {
|
if let signInWithAppleButton = self.signInWithAppleButton, self.appleSignInAllowed {
|
@ -3,7 +3,7 @@ import UIKit
|
|||||||
import AsyncDisplayKit
|
import AsyncDisplayKit
|
||||||
import Display
|
import Display
|
||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
import AuthorizationUI
|
import AuthorizationUtils
|
||||||
|
|
||||||
final class AuthorizationSequencePasswordEntryControllerNode: ASDisplayNode, UITextFieldDelegate {
|
final class AuthorizationSequencePasswordEntryControllerNode: ASDisplayNode, UITextFieldDelegate {
|
||||||
private let strings: PresentationStrings
|
private let strings: PresentationStrings
|
@ -3,7 +3,7 @@ import UIKit
|
|||||||
import AsyncDisplayKit
|
import AsyncDisplayKit
|
||||||
import Display
|
import Display
|
||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
import AuthorizationUI
|
import AuthorizationUtils
|
||||||
|
|
||||||
final class AuthorizationSequencePasswordRecoveryControllerNode: ASDisplayNode, UITextFieldDelegate {
|
final class AuthorizationSequencePasswordRecoveryControllerNode: ASDisplayNode, UITextFieldDelegate {
|
||||||
private let strings: PresentationStrings
|
private let strings: PresentationStrings
|
@ -9,7 +9,6 @@ import TelegramPresentationData
|
|||||||
import ProgressNavigationButtonNode
|
import ProgressNavigationButtonNode
|
||||||
import AccountContext
|
import AccountContext
|
||||||
import CountrySelectionUI
|
import CountrySelectionUI
|
||||||
import SettingsUI
|
|
||||||
import PhoneNumberFormat
|
import PhoneNumberFormat
|
||||||
import DebugSettingsUI
|
import DebugSettingsUI
|
||||||
|
|
||||||
@ -30,6 +29,18 @@ final class AuthorizationSequencePhoneEntryController: ViewController {
|
|||||||
|
|
||||||
private var currentData: (Int32, String?, String)?
|
private var currentData: (Int32, String?, String)?
|
||||||
|
|
||||||
|
var codeNode: ASDisplayNode {
|
||||||
|
return self.controllerNode.codeNode
|
||||||
|
}
|
||||||
|
|
||||||
|
var numberNode: ASDisplayNode {
|
||||||
|
return self.controllerNode.numberNode
|
||||||
|
}
|
||||||
|
|
||||||
|
var buttonNode: ASDisplayNode {
|
||||||
|
return self.controllerNode.buttonNode
|
||||||
|
}
|
||||||
|
|
||||||
var inProgress: Bool = false {
|
var inProgress: Bool = false {
|
||||||
didSet {
|
didSet {
|
||||||
// if self.inProgress {
|
// if self.inProgress {
|
||||||
@ -39,11 +50,14 @@ final class AuthorizationSequencePhoneEntryController: ViewController {
|
|||||||
// self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Next, style: .done, target: self, action: #selector(self.nextPressed))
|
// self.navigationItem.rightBarButtonItem = UIBarButtonItem(title: self.presentationData.strings.Common_Next, style: .done, target: self, action: #selector(self.nextPressed))
|
||||||
// }
|
// }
|
||||||
self.controllerNode.inProgress = self.inProgress
|
self.controllerNode.inProgress = self.inProgress
|
||||||
|
self.confirmationController?.inProgress = self.inProgress
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
var loginWithNumber: ((String, Bool) -> Void)?
|
var loginWithNumber: ((String, Bool) -> Void)?
|
||||||
var accountUpdated: ((UnauthorizedAccount) -> Void)?
|
var accountUpdated: ((UnauthorizedAccount) -> Void)?
|
||||||
|
|
||||||
|
weak var confirmationController: PhoneConfirmationController?
|
||||||
|
|
||||||
private let termsDisposable = MetaDisposable()
|
private let termsDisposable = MetaDisposable()
|
||||||
|
|
||||||
private let hapticFeedback = HapticFeedback()
|
private let hapticFeedback = HapticFeedback()
|
||||||
@ -98,13 +112,13 @@ final class AuthorizationSequencePhoneEntryController: ViewController {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private var shouldAnimateIn = false
|
private var shouldAnimateIn = false
|
||||||
private var transitionInArguments: (buttonFrame: CGRect, animationSnapshot: UIView, textSnapshot: UIView)?
|
private var transitionInArguments: (buttonFrame: CGRect, buttonTitle: String, animationSnapshot: UIView, textSnapshot: UIView)?
|
||||||
|
|
||||||
func animateWithSplashController(_ controller: AuthorizationSequenceSplashController) {
|
func animateWithSplashController(_ controller: AuthorizationSequenceSplashController) {
|
||||||
self.shouldAnimateIn = true
|
self.shouldAnimateIn = true
|
||||||
|
|
||||||
if let animationSnapshot = controller.animationSnapshot, let textSnapshot = controller.textSnaphot {
|
if let animationSnapshot = controller.animationSnapshot, let textSnapshot = controller.textSnaphot {
|
||||||
self.transitionInArguments = (controller.buttonFrame, animationSnapshot, textSnapshot)
|
self.transitionInArguments = (controller.buttonFrame, controller.buttonTitle, animationSnapshot, textSnapshot)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -163,8 +177,8 @@ final class AuthorizationSequencePhoneEntryController: ViewController {
|
|||||||
|
|
||||||
if self.shouldAnimateIn {
|
if self.shouldAnimateIn {
|
||||||
self.animatingIn = true
|
self.animatingIn = true
|
||||||
if let (buttonFrame, animationSnapshot, textSnapshot) = self.transitionInArguments {
|
if let (buttonFrame, buttonTitle, animationSnapshot, textSnapshot) = self.transitionInArguments {
|
||||||
self.controllerNode.willAnimateIn(buttonFrame: buttonFrame, animationSnapshot: animationSnapshot, textSnapshot: textSnapshot)
|
self.controllerNode.willAnimateIn(buttonFrame: buttonFrame, buttonTitle: buttonTitle, animationSnapshot: animationSnapshot, textSnapshot: textSnapshot)
|
||||||
}
|
}
|
||||||
Queue.mainQueue().justDispatch {
|
Queue.mainQueue().justDispatch {
|
||||||
self.controllerNode.activateInput()
|
self.controllerNode.activateInput()
|
||||||
@ -182,15 +196,23 @@ final class AuthorizationSequencePhoneEntryController: ViewController {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override func viewWillDisappear(_ animated: Bool) {
|
||||||
|
super.viewWillDisappear(animated)
|
||||||
|
|
||||||
|
if let confirmationController = self.confirmationController {
|
||||||
|
confirmationController.transitionOut()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
override func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||||
super.containerLayoutUpdated(layout, transition: transition)
|
super.containerLayoutUpdated(layout, transition: transition)
|
||||||
|
|
||||||
self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
|
self.controllerNode.containerLayoutUpdated(layout, navigationBarHeight: self.navigationLayout(layout: layout).navigationFrame.maxY, transition: transition)
|
||||||
|
|
||||||
if self.shouldAnimateIn, let inputHeight = layout.inputHeight, inputHeight > 0.0 {
|
if self.shouldAnimateIn, let inputHeight = layout.inputHeight, inputHeight > 0.0 {
|
||||||
if let (buttonFrame, animationSnapshot, textSnapshot) = self.transitionInArguments {
|
if let (buttonFrame, buttonTitle, animationSnapshot, textSnapshot) = self.transitionInArguments {
|
||||||
self.shouldAnimateIn = false
|
self.shouldAnimateIn = false
|
||||||
self.controllerNode.animateIn(buttonFrame: buttonFrame, animationSnapshot: animationSnapshot, textSnapshot: textSnapshot)
|
self.controllerNode.animateIn(buttonFrame: buttonFrame, buttonTitle: buttonTitle, animationSnapshot: animationSnapshot, textSnapshot: textSnapshot)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -217,14 +239,16 @@ final class AuthorizationSequencePhoneEntryController: ViewController {
|
|||||||
actions.append(TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {}))
|
actions.append(TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Common_OK, action: {}))
|
||||||
self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.Login_PhoneNumberAlreadyAuthorized, actions: actions), in: .window(.root))
|
self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: nil, text: self.presentationData.strings.Login_PhoneNumberAlreadyAuthorized, actions: actions), in: .window(.root))
|
||||||
} else {
|
} else {
|
||||||
var actions: [TextAlertAction] = []
|
let (code, formattedNumber) = self.controllerNode.formattedCodeAndNumber
|
||||||
actions.append(TextAlertAction(type: .genericAction, title: self.presentationData.strings.Login_Edit, action: {}))
|
|
||||||
actions.append(TextAlertAction(type: .defaultAction, title: self.presentationData.strings.Login_Yes, action: { [weak self] in
|
let confirmationController = PhoneConfirmationController(theme: self.presentationData.theme, strings: self.presentationData.strings, code: code, number: formattedNumber, sourceController: self)
|
||||||
|
confirmationController.proceed = { [weak self] in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.loginWithNumber?(strongSelf.controllerNode.currentNumber, strongSelf.controllerNode.syncContacts)
|
strongSelf.loginWithNumber?(strongSelf.controllerNode.currentNumber, strongSelf.controllerNode.syncContacts)
|
||||||
}
|
}
|
||||||
}))
|
}
|
||||||
self.present(standardTextAlertController(theme: AlertControllerTheme(presentationData: self.presentationData), title: logInNumber, text: self.presentationData.strings.Login_PhoneNumberConfirmation, actions: actions), in: .window(.root))
|
(self.navigationController as? NavigationController)?.presentOverlay(controller: confirmationController, inGlobal: true, blockInteraction: true)
|
||||||
|
self.confirmationController = confirmationController
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
self.hapticFeedback.error()
|
self.hapticFeedback.error()
|
@ -6,7 +6,6 @@ import TelegramCore
|
|||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
import PhoneInputNode
|
import PhoneInputNode
|
||||||
import CountrySelectionUI
|
import CountrySelectionUI
|
||||||
import AuthorizationUI
|
|
||||||
import QrCode
|
import QrCode
|
||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import Postbox
|
import Postbox
|
||||||
@ -14,6 +13,8 @@ import AccountContext
|
|||||||
import AnimatedStickerNode
|
import AnimatedStickerNode
|
||||||
import TelegramAnimatedStickerNode
|
import TelegramAnimatedStickerNode
|
||||||
import SolidRoundedButtonNode
|
import SolidRoundedButtonNode
|
||||||
|
import AuthorizationUtils
|
||||||
|
import ManagedAnimationNode
|
||||||
|
|
||||||
private final class PhoneAndCountryNode: ASDisplayNode {
|
private final class PhoneAndCountryNode: ASDisplayNode {
|
||||||
let strings: PresentationStrings
|
let strings: PresentationStrings
|
||||||
@ -23,9 +24,13 @@ private final class PhoneAndCountryNode: ASDisplayNode {
|
|||||||
|
|
||||||
var selectCountryCode: (() -> Void)?
|
var selectCountryCode: (() -> Void)?
|
||||||
var checkPhone: (() -> Void)?
|
var checkPhone: (() -> Void)?
|
||||||
|
var hasNumberUpdated: ((Bool) -> Void)?
|
||||||
|
var numberUpdated: (() -> Void)?
|
||||||
|
|
||||||
var preferredCountryIdForCode: [String: String] = [:]
|
var preferredCountryIdForCode: [String: String] = [:]
|
||||||
|
|
||||||
|
var hasCountry = false
|
||||||
|
|
||||||
init(strings: PresentationStrings, theme: PresentationTheme) {
|
init(strings: PresentationStrings, theme: PresentationTheme) {
|
||||||
self.strings = strings
|
self.strings = strings
|
||||||
|
|
||||||
@ -122,6 +127,7 @@ private final class PhoneAndCountryNode: ASDisplayNode {
|
|||||||
let flagString = emojiFlagForISOCountryCode(country.id)
|
let flagString = emojiFlagForISOCountryCode(country.id)
|
||||||
let localizedName: String = AuthorizationSequenceCountrySelectionController.lookupCountryNameById(country.id, strings: strongSelf.strings) ?? country.name
|
let localizedName: String = AuthorizationSequenceCountrySelectionController.lookupCountryNameById(country.id, strings: strongSelf.strings) ?? country.name
|
||||||
strongSelf.countryButton.setTitle("\(flagString) \(localizedName)", with: Font.regular(20.0), with: theme.list.itemAccentColor, for: [])
|
strongSelf.countryButton.setTitle("\(flagString) \(localizedName)", with: Font.regular(20.0), with: theme.list.itemAccentColor, for: [])
|
||||||
|
strongSelf.hasCountry = true
|
||||||
|
|
||||||
let maskFont = Font.with(size: 20.0, design: .regular, traits: [.monospacedNumbers])
|
let maskFont = Font.with(size: 20.0, design: .regular, traits: [.monospacedNumbers])
|
||||||
if let mask = AuthorizationSequenceCountrySelectionController.lookupPatternByNumber(number, preferredCountries: strongSelf.preferredCountryIdForCode).flatMap({ NSAttributedString(string: $0, font: maskFont, textColor: theme.list.itemPlaceholderTextColor) }) {
|
if let mask = AuthorizationSequenceCountrySelectionController.lookupPatternByNumber(number, preferredCountries: strongSelf.preferredCountryIdForCode).flatMap({ NSAttributedString(string: $0, font: maskFont, textColor: theme.list.itemPlaceholderTextColor) }) {
|
||||||
@ -140,6 +146,14 @@ private final class PhoneAndCountryNode: ASDisplayNode {
|
|||||||
self.phoneInputNode.numberTextUpdated = { [weak self] number in
|
self.phoneInputNode.numberTextUpdated = { [weak self] number in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
let _ = processNumberChange(strongSelf.phoneInputNode.number)
|
let _ = processNumberChange(strongSelf.phoneInputNode.number)
|
||||||
|
|
||||||
|
strongSelf.numberUpdated?()
|
||||||
|
|
||||||
|
if strongSelf.hasCountry {
|
||||||
|
strongSelf.hasNumberUpdated?(!strongSelf.phoneInputNode.codeAndNumber.2.isEmpty)
|
||||||
|
} else {
|
||||||
|
strongSelf.hasNumberUpdated?(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -149,11 +163,14 @@ private final class PhoneAndCountryNode: ASDisplayNode {
|
|||||||
strongSelf.preferredCountryIdForCode[code] = name
|
strongSelf.preferredCountryIdForCode[code] = name
|
||||||
}
|
}
|
||||||
|
|
||||||
|
strongSelf.numberUpdated?()
|
||||||
|
|
||||||
if processNumberChange(strongSelf.phoneInputNode.number) {
|
if processNumberChange(strongSelf.phoneInputNode.number) {
|
||||||
} else if let code = Int(code), let name = name, let countryName = countryCodeAndIdToName[CountryCodeAndId(code: code, id: name)] {
|
} else if let code = Int(code), let name = name, let countryName = countryCodeAndIdToName[CountryCodeAndId(code: code, id: name)] {
|
||||||
let flagString = emojiFlagForISOCountryCode(name)
|
let flagString = emojiFlagForISOCountryCode(name)
|
||||||
let localizedName: String = AuthorizationSequenceCountrySelectionController.lookupCountryNameById(name, strings: strongSelf.strings) ?? countryName
|
let localizedName: String = AuthorizationSequenceCountrySelectionController.lookupCountryNameById(name, strings: strongSelf.strings) ?? countryName
|
||||||
strongSelf.countryButton.setTitle("\(flagString) \(localizedName)", with: Font.regular(20.0), with: theme.list.itemAccentColor, for: [])
|
strongSelf.countryButton.setTitle("\(flagString) \(localizedName)", with: Font.regular(20.0), with: theme.list.itemAccentColor, for: [])
|
||||||
|
strongSelf.hasCountry = true
|
||||||
|
|
||||||
if strongSelf.phoneInputNode.mask == nil {
|
if strongSelf.phoneInputNode.mask == nil {
|
||||||
strongSelf.phoneInputNode.numberField.textField.attributedPlaceholder = NSAttributedString(string: strings.Login_PhonePlaceholder, font: Font.regular(20.0), textColor: theme.list.itemPlaceholderTextColor)
|
strongSelf.phoneInputNode.numberField.textField.attributedPlaceholder = NSAttributedString(string: strings.Login_PhonePlaceholder, font: Font.regular(20.0), textColor: theme.list.itemPlaceholderTextColor)
|
||||||
@ -162,15 +179,23 @@ private final class PhoneAndCountryNode: ASDisplayNode {
|
|||||||
let flagString = emojiFlagForISOCountryCode(countryId)
|
let flagString = emojiFlagForISOCountryCode(countryId)
|
||||||
let localizedName: String = AuthorizationSequenceCountrySelectionController.lookupCountryNameById(countryId, strings: strongSelf.strings) ?? countryName
|
let localizedName: String = AuthorizationSequenceCountrySelectionController.lookupCountryNameById(countryId, strings: strongSelf.strings) ?? countryName
|
||||||
strongSelf.countryButton.setTitle("\(flagString) \(localizedName)", with: Font.regular(20.0), with: theme.list.itemAccentColor, for: [])
|
strongSelf.countryButton.setTitle("\(flagString) \(localizedName)", with: Font.regular(20.0), with: theme.list.itemAccentColor, for: [])
|
||||||
|
strongSelf.hasCountry = true
|
||||||
|
|
||||||
if strongSelf.phoneInputNode.mask == nil {
|
if strongSelf.phoneInputNode.mask == nil {
|
||||||
strongSelf.phoneInputNode.numberField.textField.attributedPlaceholder = NSAttributedString(string: strings.Login_PhonePlaceholder, font: Font.regular(20.0), textColor: theme.list.itemPlaceholderTextColor)
|
strongSelf.phoneInputNode.numberField.textField.attributedPlaceholder = NSAttributedString(string: strings.Login_PhonePlaceholder, font: Font.regular(20.0), textColor: theme.list.itemPlaceholderTextColor)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
strongSelf.hasCountry = false
|
||||||
strongSelf.countryButton.setTitle(strings.Login_SelectCountry, 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.mask = nil
|
||||||
strongSelf.phoneInputNode.numberField.textField.attributedPlaceholder = NSAttributedString(string: strings.Login_PhonePlaceholder, font: Font.regular(20.0), textColor: theme.list.itemPlaceholderTextColor)
|
strongSelf.phoneInputNode.numberField.textField.attributedPlaceholder = NSAttributedString(string: strings.Login_PhonePlaceholder, font: Font.regular(20.0), textColor: theme.list.itemPlaceholderTextColor)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if strongSelf.hasCountry {
|
||||||
|
strongSelf.hasNumberUpdated?(!strongSelf.phoneInputNode.codeAndNumber.2.isEmpty)
|
||||||
|
} else {
|
||||||
|
strongSelf.hasNumberUpdated?(false)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -253,6 +278,7 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
|||||||
private let hasOtherAccounts: Bool
|
private let hasOtherAccounts: Bool
|
||||||
|
|
||||||
private let animationNode: AnimatedStickerNode
|
private let animationNode: AnimatedStickerNode
|
||||||
|
private let managedAnimationNode: ManagedPhoneAnimationNode
|
||||||
private let titleNode: ASTextNode
|
private let titleNode: ASTextNode
|
||||||
private let noticeNode: ASTextNode
|
private let noticeNode: ASTextNode
|
||||||
private let phoneAndCountryNode: PhoneAndCountryNode
|
private let phoneAndCountryNode: PhoneAndCountryNode
|
||||||
@ -278,6 +304,10 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var formattedCodeAndNumber: (String, String) {
|
||||||
|
return self.phoneAndCountryNode.phoneInputNode.formattedCodeAndNumber
|
||||||
|
}
|
||||||
|
|
||||||
var syncContacts: Bool {
|
var syncContacts: Bool {
|
||||||
get {
|
get {
|
||||||
if self.hasOtherAccounts {
|
if self.hasOtherAccounts {
|
||||||
@ -307,6 +337,18 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var codeNode: ASDisplayNode {
|
||||||
|
return self.phoneAndCountryNode.phoneInputNode.countryCodeField
|
||||||
|
}
|
||||||
|
|
||||||
|
var numberNode: ASDisplayNode {
|
||||||
|
return self.phoneAndCountryNode.phoneInputNode.numberField
|
||||||
|
}
|
||||||
|
|
||||||
|
var buttonNode: ASDisplayNode {
|
||||||
|
return self.proceedNode
|
||||||
|
}
|
||||||
|
|
||||||
init(sharedContext: SharedAccountContext, account: UnauthorizedAccount, strings: PresentationStrings, theme: PresentationTheme, debugAction: @escaping () -> Void, hasOtherAccounts: Bool) {
|
init(sharedContext: SharedAccountContext, account: UnauthorizedAccount, strings: PresentationStrings, theme: PresentationTheme, debugAction: @escaping () -> Void, hasOtherAccounts: Bool) {
|
||||||
self.sharedContext = sharedContext
|
self.sharedContext = sharedContext
|
||||||
self.account = account
|
self.account = account
|
||||||
@ -320,6 +362,9 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
|||||||
self.animationNode.setup(source: AnimatedStickerNodeLocalFileSource(name: "IntroPhone"), width: 256, height: 256, playbackMode: .once, 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.animationNode.visibility = true
|
||||||
|
|
||||||
|
self.managedAnimationNode = ManagedPhoneAnimationNode()
|
||||||
|
self.managedAnimationNode.isHidden = true
|
||||||
|
|
||||||
self.titleNode = ASTextNode()
|
self.titleNode = ASTextNode()
|
||||||
self.titleNode.isUserInteractionEnabled = true
|
self.titleNode.isUserInteractionEnabled = true
|
||||||
self.titleNode.displaysAsynchronously = false
|
self.titleNode.displaysAsynchronously = false
|
||||||
@ -338,6 +383,7 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
|||||||
|
|
||||||
self.proceedNode = SolidRoundedButtonNode(title: self.strings.Login_Continue, theme: SolidRoundedButtonTheme(theme: self.theme), height: 50.0, cornerRadius: 11.0, gloss: false)
|
self.proceedNode = SolidRoundedButtonNode(title: self.strings.Login_Continue, theme: SolidRoundedButtonTheme(theme: self.theme), height: 50.0, cornerRadius: 11.0, gloss: false)
|
||||||
self.proceedNode.progressType = .embedded
|
self.proceedNode.progressType = .embedded
|
||||||
|
self.proceedNode.isEnabled = false
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
@ -353,6 +399,7 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
|||||||
self.addSubnode(self.contactSyncNode)
|
self.addSubnode(self.contactSyncNode)
|
||||||
self.addSubnode(self.proceedNode)
|
self.addSubnode(self.proceedNode)
|
||||||
self.addSubnode(self.animationNode)
|
self.addSubnode(self.animationNode)
|
||||||
|
self.addSubnode(self.managedAnimationNode)
|
||||||
self.contactSyncNode.isHidden = true
|
self.contactSyncNode.isHidden = true
|
||||||
|
|
||||||
self.phoneAndCountryNode.selectCountryCode = { [weak self] in
|
self.phoneAndCountryNode.selectCountryCode = { [weak self] in
|
||||||
@ -361,6 +408,16 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
|||||||
self.phoneAndCountryNode.checkPhone = { [weak self] in
|
self.phoneAndCountryNode.checkPhone = { [weak self] in
|
||||||
self?.checkPhone?()
|
self?.checkPhone?()
|
||||||
}
|
}
|
||||||
|
self.phoneAndCountryNode.hasNumberUpdated = { [weak self] hasNumber in
|
||||||
|
self?.proceedNode.isEnabled = hasNumber
|
||||||
|
}
|
||||||
|
self.phoneAndCountryNode.numberUpdated = { [weak self] in
|
||||||
|
if let strongSelf = self, !strongSelf.managedAnimationNode.isHidden {
|
||||||
|
if let state = ManagedPhoneAnimationState.allCases.randomElement() {
|
||||||
|
strongSelf.managedAnimationNode.enqueue(state)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
self.tokenEventsDisposable.set((account.updateLoginTokenEvents
|
self.tokenEventsDisposable.set((account.updateLoginTokenEvents
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] _ in
|
|> deliverOnMainQueue).start(next: { [weak self] _ in
|
||||||
@ -370,6 +427,11 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
|||||||
self.proceedNode.pressed = { [weak self] in
|
self.proceedNode.pressed = { [weak self] in
|
||||||
self?.checkPhone?()
|
self?.checkPhone?()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.animationNode.completed = { [weak self] _ in
|
||||||
|
self?.animationNode.isHidden = true
|
||||||
|
self?.managedAnimationNode.isHidden = false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
@ -390,9 +452,11 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
|||||||
private var textSnapshotView: UIView?
|
private var textSnapshotView: UIView?
|
||||||
private var forcedButtonFrame: CGRect?
|
private var forcedButtonFrame: CGRect?
|
||||||
|
|
||||||
func willAnimateIn(buttonFrame: CGRect, animationSnapshot: UIView, textSnapshot: UIView) {
|
func willAnimateIn(buttonFrame: CGRect, buttonTitle: String, animationSnapshot: UIView, textSnapshot: UIView) {
|
||||||
self.proceedNode.frame = buttonFrame
|
self.proceedNode.frame = buttonFrame
|
||||||
self.proceedNode.title = "Start Messaging"
|
|
||||||
|
self.proceedNode.isEnabled = true
|
||||||
|
self.proceedNode.title = buttonTitle
|
||||||
|
|
||||||
self.animationSnapshotView = animationSnapshot
|
self.animationSnapshotView = animationSnapshot
|
||||||
self.view.insertSubview(animationSnapshot, at: 0)
|
self.view.insertSubview(animationSnapshot, at: 0)
|
||||||
@ -413,7 +477,7 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func animateIn(buttonFrame: CGRect, animationSnapshot: UIView, textSnapshot: UIView) {
|
func animateIn(buttonFrame: CGRect, buttonTitle: String, animationSnapshot: UIView, textSnapshot: UIView) {
|
||||||
self.proceedNode.animateTitle(to: self.strings.Login_Continue)
|
self.proceedNode.animateTitle(to: self.strings.Login_Continue)
|
||||||
|
|
||||||
let duration: Double = 0.3
|
let duration: Double = 0.3
|
||||||
@ -423,6 +487,7 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
|||||||
self?.animationSnapshotView = nil
|
self?.animationSnapshotView = nil
|
||||||
})
|
})
|
||||||
self.animationSnapshotView?.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -100.0), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true)
|
self.animationSnapshotView?.layer.animatePosition(from: CGPoint(), to: CGPoint(x: 0.0, y: -100.0), duration: 0.3, timingFunction: kCAMediaTimingFunctionSpring, removeOnCompletion: false, additive: true)
|
||||||
|
self.animationSnapshotView?.layer.animateScale(from: 1.0, to: 0.3, duration: 0.4)
|
||||||
|
|
||||||
self.textSnapshotView?.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak self] _ in
|
self.textSnapshotView?.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak self] _ in
|
||||||
self?.textSnapshotView?.removeFromSuperview()
|
self?.textSnapshotView?.removeFromSuperview()
|
||||||
@ -438,6 +503,8 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
|||||||
self.contactSyncNode
|
self.contactSyncNode
|
||||||
]
|
]
|
||||||
|
|
||||||
|
self.animationNode.layer.animateScale(from: 0.3, to: 1.0, duration: 0.3)
|
||||||
|
|
||||||
for node in nodes {
|
for node in nodes {
|
||||||
node.alpha = 1.0
|
node.alpha = 1.0
|
||||||
node.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration)
|
node.layer.animateAlpha(from: 0.0, to: 1.0, duration: duration)
|
||||||
@ -492,6 +559,8 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
|||||||
self.animationNode.updateLayout(size: animationSize)
|
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 - 80.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)
|
||||||
|
|
||||||
|
transition.updateFrame(node: self.managedAnimationNode, frame: self.animationNode.frame)
|
||||||
}
|
}
|
||||||
|
|
||||||
func activateInput() {
|
func activateInput() {
|
||||||
@ -601,3 +670,366 @@ final class AuthorizationSequencePhoneEntryControllerNode: ASDisplayNode {
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
final class PhoneConfirmationController: ViewController {
|
||||||
|
private var controllerNode: Node {
|
||||||
|
return self.displayNode as! Node
|
||||||
|
}
|
||||||
|
|
||||||
|
private let theme: PresentationTheme
|
||||||
|
private let strings: PresentationStrings
|
||||||
|
private let code: String
|
||||||
|
private let number: String
|
||||||
|
private weak var sourceController: AuthorizationSequencePhoneEntryController?
|
||||||
|
|
||||||
|
var inProgress: Bool = false {
|
||||||
|
didSet {
|
||||||
|
if self.inProgress != oldValue {
|
||||||
|
if self.inProgress {
|
||||||
|
self.controllerNode.proceedNode.transitionToProgress()
|
||||||
|
} else {
|
||||||
|
self.controllerNode.proceedNode.transitionFromProgress()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var proceed: () -> Void = {}
|
||||||
|
|
||||||
|
class Node: ASDisplayNode {
|
||||||
|
private let dimNode: ASDisplayNode
|
||||||
|
private let backgroundNode: ASDisplayNode
|
||||||
|
|
||||||
|
private let codeSourceNode: ImmediateTextNode
|
||||||
|
private let phoneSourceNode: ImmediateTextNode
|
||||||
|
|
||||||
|
private let codeTargetNode: ImmediateTextNode
|
||||||
|
private let phoneTargetNode: ImmediateTextNode
|
||||||
|
|
||||||
|
private let textNode: ImmediateTextNode
|
||||||
|
|
||||||
|
private let cancelButton: HighlightableButtonNode
|
||||||
|
fileprivate let proceedNode: SolidRoundedButtonNode
|
||||||
|
|
||||||
|
var proceed: () -> Void = {}
|
||||||
|
var cancel: () -> Void = {}
|
||||||
|
|
||||||
|
init(theme: PresentationTheme, strings: PresentationStrings, code: String, number: String) {
|
||||||
|
self.dimNode = ASDisplayNode()
|
||||||
|
self.dimNode.backgroundColor = UIColor(white: 0.0, alpha: 0.4)
|
||||||
|
|
||||||
|
self.backgroundNode = ASDisplayNode()
|
||||||
|
self.backgroundNode.backgroundColor = theme.list.plainBackgroundColor
|
||||||
|
self.backgroundNode.cornerRadius = 11.0
|
||||||
|
|
||||||
|
self.textNode = ImmediateTextNode()
|
||||||
|
self.textNode.displaysAsynchronously = false
|
||||||
|
self.textNode.attributedText = NSAttributedString(string: strings.Login_PhoneNumberConfirmation, font: Font.regular(17.0), textColor: theme.list.itemPrimaryTextColor)
|
||||||
|
self.textNode.textAlignment = .center
|
||||||
|
|
||||||
|
self.cancelButton = HighlightableButtonNode()
|
||||||
|
self.cancelButton.setTitle(strings.Login_Edit, with: Font.regular(19.0), with: theme.list.itemAccentColor, for: .normal)
|
||||||
|
|
||||||
|
self.proceedNode = SolidRoundedButtonNode(title: strings.Login_Continue, theme: SolidRoundedButtonTheme(theme: theme), height: 50.0, cornerRadius: 11.0, gloss: false)
|
||||||
|
self.proceedNode.progressType = .embedded
|
||||||
|
|
||||||
|
let font = Font.with(size: 20.0, design: .regular, traits: [.monospacedNumbers])
|
||||||
|
let largeFont = Font.with(size: 34.0, design: .regular, weight: .bold, traits: [.monospacedNumbers])
|
||||||
|
|
||||||
|
self.codeSourceNode = ImmediateTextNode()
|
||||||
|
self.codeSourceNode.alpha = 0.0
|
||||||
|
self.codeSourceNode.displaysAsynchronously = false
|
||||||
|
self.codeSourceNode.attributedText = NSAttributedString(string: code, font: font, textColor: theme.list.itemPrimaryTextColor)
|
||||||
|
|
||||||
|
self.phoneSourceNode = ImmediateTextNode()
|
||||||
|
self.phoneSourceNode.alpha = 0.0
|
||||||
|
self.phoneSourceNode.displaysAsynchronously = false
|
||||||
|
|
||||||
|
let sourceString = NSMutableAttributedString(string: number, font: font, textColor: theme.list.itemPrimaryTextColor)
|
||||||
|
sourceString.addAttribute(NSAttributedString.Key.kern, value: 1.6, range: NSRange(location: 0, length: sourceString.length))
|
||||||
|
self.phoneSourceNode.attributedText = sourceString
|
||||||
|
|
||||||
|
self.codeTargetNode = ImmediateTextNode()
|
||||||
|
self.codeTargetNode.displaysAsynchronously = false
|
||||||
|
self.codeTargetNode.attributedText = NSAttributedString(string: code, font: largeFont, textColor: theme.list.itemPrimaryTextColor)
|
||||||
|
|
||||||
|
|
||||||
|
self.phoneTargetNode = ImmediateTextNode()
|
||||||
|
self.phoneTargetNode.displaysAsynchronously = false
|
||||||
|
|
||||||
|
let targetString = NSMutableAttributedString(string: number, font: largeFont, textColor: theme.list.itemPrimaryTextColor)
|
||||||
|
targetString.addAttribute(NSAttributedString.Key.kern, value: 1.6, range: NSRange(location: 0, length: sourceString.length))
|
||||||
|
self.phoneTargetNode.attributedText = targetString
|
||||||
|
|
||||||
|
super.init()
|
||||||
|
|
||||||
|
self.clipsToBounds = false
|
||||||
|
|
||||||
|
self.addSubnode(self.dimNode)
|
||||||
|
self.addSubnode(self.backgroundNode)
|
||||||
|
|
||||||
|
self.addSubnode(self.codeSourceNode)
|
||||||
|
self.addSubnode(self.phoneSourceNode)
|
||||||
|
|
||||||
|
self.addSubnode(self.codeTargetNode)
|
||||||
|
self.addSubnode(self.phoneTargetNode)
|
||||||
|
|
||||||
|
self.addSubnode(self.textNode)
|
||||||
|
|
||||||
|
self.addSubnode(self.cancelButton)
|
||||||
|
self.addSubnode(self.proceedNode)
|
||||||
|
|
||||||
|
self.cancelButton.addTarget(self, action: #selector(self.cancelPressed), forControlEvents: .touchUpInside)
|
||||||
|
self.proceedNode.pressed = { [weak self] in
|
||||||
|
self?.proceed()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override func didLoad() {
|
||||||
|
super.didLoad()
|
||||||
|
|
||||||
|
self.dimNode.view.addGestureRecognizer(UITapGestureRecognizer(target: self, action: #selector(self.dimTapped)))
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func dimTapped() {
|
||||||
|
self.cancelPressed()
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func cancelPressed() {
|
||||||
|
self.dimNode.isUserInteractionEnabled = false
|
||||||
|
self.cancel()
|
||||||
|
}
|
||||||
|
|
||||||
|
func animateIn(codeNode: ASDisplayNode, numberNode: ASDisplayNode, buttonNode: ASDisplayNode) {
|
||||||
|
let codeFrame = codeNode.convert(codeNode.bounds, to: nil)
|
||||||
|
let numberFrame = numberNode.convert(numberNode.bounds, to: nil)
|
||||||
|
let buttonFrame = buttonNode.convert(buttonNode.bounds, to: nil)
|
||||||
|
|
||||||
|
codeNode.isHidden = true
|
||||||
|
numberNode.isHidden = true
|
||||||
|
buttonNode.isHidden = true
|
||||||
|
|
||||||
|
self.dimNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||||
|
|
||||||
|
let codeSize = self.codeSourceNode.updateLayout(self.frame.size)
|
||||||
|
self.codeSourceNode.frame = CGRect(origin: CGPoint(x: codeFrame.midX - codeSize.width / 2.0, y: codeFrame.midY - codeSize.height / 2.0), size: codeSize)
|
||||||
|
|
||||||
|
let numberSize = self.phoneSourceNode.updateLayout(self.frame.size)
|
||||||
|
self.phoneSourceNode.frame = CGRect(origin: CGPoint(x: numberFrame.minX, y: numberFrame.midY - numberSize.height / 2.0), size: numberSize)
|
||||||
|
|
||||||
|
let targetScale = codeSize.height / self.codeTargetNode.frame.height
|
||||||
|
let sourceScale = self.codeTargetNode.frame.height / codeSize.height
|
||||||
|
|
||||||
|
self.codeSourceNode.layer.animateScale(from: 1.0, to: sourceScale, duration: 0.3)
|
||||||
|
self.codeSourceNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3)
|
||||||
|
self.codeSourceNode.layer.animatePosition(from: self.codeSourceNode.position, to: self.codeTargetNode.position, duration: 0.3)
|
||||||
|
|
||||||
|
self.phoneSourceNode.layer.animateScale(from: 1.0, to: sourceScale, duration: 0.3)
|
||||||
|
self.phoneSourceNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3)
|
||||||
|
self.phoneSourceNode.layer.animatePosition(from: self.phoneSourceNode.position, to: self.phoneTargetNode.position, duration: 0.3)
|
||||||
|
|
||||||
|
self.codeTargetNode.layer.animateScale(from: targetScale, to: 1.0, duration: 0.3)
|
||||||
|
self.codeTargetNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||||
|
self.codeTargetNode.layer.animatePosition(from: self.codeSourceNode.position, to: self.codeTargetNode.position, duration: 0.3)
|
||||||
|
|
||||||
|
self.phoneTargetNode.layer.animateScale(from: targetScale, to: 1.0, duration: 0.3)
|
||||||
|
self.phoneTargetNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||||
|
self.phoneTargetNode.layer.animatePosition(from: self.phoneSourceNode.position, to: self.phoneTargetNode.position, duration: 0.3)
|
||||||
|
|
||||||
|
self.backgroundNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.1)
|
||||||
|
self.backgroundNode.layer.animateFrame(from: CGRect(origin: CGPoint(x: 14.0, y: codeFrame.minY), size: CGSize(width: self.backgroundNode.frame.width - 12.0, height: buttonFrame.maxY + 18.0 - codeFrame.minY)), to: self.backgroundNode.frame, duration: 0.3)
|
||||||
|
|
||||||
|
self.textNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||||
|
self.textNode.layer.animateScale(from: 0.5, to: 1.0, duration: 0.3)
|
||||||
|
self.textNode.layer.animatePosition(from: CGPoint(x: -100.0, y: -45.0), to: CGPoint(), duration: 0.3, additive: true)
|
||||||
|
|
||||||
|
self.cancelButton.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||||
|
self.cancelButton.layer.animateScale(from: 0.5, to: 1.0, duration: 0.3)
|
||||||
|
self.cancelButton.layer.animatePosition(from: CGPoint(x: -100.0, y: -70.0), to: CGPoint(), duration: 0.3, additive: true)
|
||||||
|
|
||||||
|
self.proceedNode.layer.animatePosition(from: buttonFrame.center, to: self.proceedNode.position, duration: 0.3)
|
||||||
|
}
|
||||||
|
|
||||||
|
func animateOut(codeNode: ASDisplayNode, numberNode: ASDisplayNode, buttonNode: ASDisplayNode, completion: @escaping () -> Void) {
|
||||||
|
let codeFrame = codeNode.convert(codeNode.bounds, to: nil)
|
||||||
|
let numberFrame = numberNode.convert(numberNode.bounds, to: nil)
|
||||||
|
let buttonFrame = buttonNode.convert(buttonNode.bounds, to: nil)
|
||||||
|
|
||||||
|
self.dimNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||||
|
|
||||||
|
let codeSize = self.codeSourceNode.updateLayout(self.frame.size)
|
||||||
|
self.codeSourceNode.frame = CGRect(origin: CGPoint(x: codeFrame.midX - codeSize.width / 2.0, y: codeFrame.midY - codeSize.height / 2.0), size: codeSize)
|
||||||
|
|
||||||
|
let numberSize = self.phoneSourceNode.updateLayout(self.frame.size)
|
||||||
|
self.phoneSourceNode.frame = CGRect(origin: CGPoint(x: numberFrame.minX, y: numberFrame.midY - numberSize.height / 2.0), size: numberSize)
|
||||||
|
|
||||||
|
let targetScale = codeSize.height / self.codeTargetNode.frame.height
|
||||||
|
let sourceScale = self.codeTargetNode.frame.height / codeSize.height
|
||||||
|
|
||||||
|
self.codeSourceNode.layer.animateScale(from: sourceScale, to: 1.0, duration: 0.3)
|
||||||
|
self.codeSourceNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||||
|
self.codeSourceNode.layer.animatePosition(from: self.codeTargetNode.position, to: self.codeSourceNode.position, duration: 0.3)
|
||||||
|
|
||||||
|
self.phoneSourceNode.layer.animateScale(from: sourceScale, to: 1.0, duration: 0.3)
|
||||||
|
self.phoneSourceNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.3)
|
||||||
|
self.phoneSourceNode.layer.animatePosition(from: self.phoneTargetNode.position, to: self.phoneSourceNode.position, duration: 0.3)
|
||||||
|
|
||||||
|
self.codeTargetNode.layer.animateScale(from: 1.0, to: targetScale, duration: 0.3)
|
||||||
|
self.codeTargetNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false)
|
||||||
|
self.codeTargetNode.layer.animatePosition(from: self.codeTargetNode.position, to: self.codeSourceNode.position, duration: 0.3)
|
||||||
|
|
||||||
|
Queue.mainQueue().after(0.25) {
|
||||||
|
codeNode.isHidden = false
|
||||||
|
numberNode.isHidden = false
|
||||||
|
buttonNode.isHidden = false
|
||||||
|
}
|
||||||
|
|
||||||
|
self.phoneTargetNode.layer.animateScale(from: 1.0, to: targetScale, duration: 0.3)
|
||||||
|
self.phoneTargetNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.3, removeOnCompletion: false, completion: { _ in
|
||||||
|
completion()
|
||||||
|
})
|
||||||
|
self.phoneTargetNode.layer.animatePosition(from: self.phoneTargetNode.position, to: self.phoneSourceNode.position, duration: 0.3)
|
||||||
|
|
||||||
|
self.backgroundNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, delay: 0.1, removeOnCompletion: false)
|
||||||
|
self.backgroundNode.layer.animateFrame(from: self.backgroundNode.frame, to: CGRect(origin: CGPoint(x: 14.0, y: codeFrame.minY), size: CGSize(width: self.backgroundNode.frame.width - 12.0, height: buttonFrame.maxY + 18.0 - codeFrame.minY)), duration: 0.3)
|
||||||
|
|
||||||
|
self.textNode.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||||
|
self.textNode.layer.animateScale(from: 1.0, to: 0.5, duration: 0.3, removeOnCompletion: false)
|
||||||
|
self.textNode.layer.animatePosition(from: CGPoint(), to: CGPoint(x: -100.0, y: -45.0), duration: 0.3, removeOnCompletion: false, additive: true)
|
||||||
|
|
||||||
|
self.cancelButton.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false)
|
||||||
|
self.cancelButton.layer.animateScale(from: 1.0, to: 0.5, duration: 0.3, removeOnCompletion: false)
|
||||||
|
self.cancelButton.layer.animatePosition(from: CGPoint(), to: CGPoint(x: -100.0, y: -70.0), duration: 0.3, removeOnCompletion: false, additive: true)
|
||||||
|
|
||||||
|
self.proceedNode.layer.animatePosition(from: self.proceedNode.position, to: buttonFrame.center, duration: 0.3, removeOnCompletion: false)
|
||||||
|
}
|
||||||
|
|
||||||
|
func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||||
|
let sideInset: CGFloat = 8.0
|
||||||
|
let innerInset: CGFloat = 18.0
|
||||||
|
|
||||||
|
transition.updateFrame(node: self.dimNode, frame: CGRect(origin: CGPoint(x: -layout.size.width, y: 0.0), size: CGSize(width: layout.size.width * 3.0, height: layout.size.height)))
|
||||||
|
|
||||||
|
let backgroundSize = CGSize(width: layout.size.width - sideInset * 2.0, height: 243.0)
|
||||||
|
let backgroundFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((layout.size.width - backgroundSize.width) / 2.0), y: layout.size.height - backgroundSize.height - 260.0), size: backgroundSize)
|
||||||
|
transition.updateFrame(node: self.backgroundNode, frame: backgroundFrame)
|
||||||
|
|
||||||
|
let codeSize = self.codeTargetNode.updateLayout(backgroundSize)
|
||||||
|
let numberSize = self.phoneTargetNode.updateLayout(backgroundSize)
|
||||||
|
|
||||||
|
let totalWidth = codeSize.width + numberSize.width + 10.0
|
||||||
|
|
||||||
|
let codeFrame = CGRect(origin: CGPoint(x: floorToScreenPixels((backgroundSize.width - totalWidth) / 2.0), y: 30.0), size: codeSize)
|
||||||
|
transition.updateFrame(node: self.codeTargetNode, frame: codeFrame.offsetBy(dx: backgroundFrame.minX, dy: backgroundFrame.minY))
|
||||||
|
|
||||||
|
let numberFrame = CGRect(origin: CGPoint(x: codeFrame.maxX + 10.0, y: 30.0), size: numberSize)
|
||||||
|
transition.updateFrame(node: self.phoneTargetNode, frame: numberFrame.offsetBy(dx: backgroundFrame.minX, dy: backgroundFrame.minY))
|
||||||
|
|
||||||
|
let textSize = self.textNode.updateLayout(backgroundSize)
|
||||||
|
transition.updateFrame(node: self.textNode, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((backgroundSize.width - textSize.width) / 2.0), y: 88.0), size: textSize).offsetBy(dx: backgroundFrame.minX, dy: backgroundFrame.minY))
|
||||||
|
|
||||||
|
let proceedWidth = backgroundSize.width - 16.0 * 2.0
|
||||||
|
let proceedHeight = self.proceedNode.updateLayout(width: proceedWidth, transition: transition)
|
||||||
|
transition.updateFrame(node: self.proceedNode, frame: CGRect(origin: CGPoint(x: innerInset, y: backgroundSize.height - proceedHeight - innerInset), size: CGSize(width: proceedWidth, height: proceedHeight)).offsetBy(dx: backgroundFrame.minX, dy: backgroundFrame.minY))
|
||||||
|
|
||||||
|
let cancelSize = self.cancelButton.measure(layout.size)
|
||||||
|
transition.updateFrame(node: self.cancelButton, frame: CGRect(origin: CGPoint(x: floorToScreenPixels((backgroundSize.width - cancelSize.width) / 2.0), y: backgroundSize.height - proceedHeight - innerInset - cancelSize.height - 25.0), size: cancelSize).offsetBy(dx: backgroundFrame.minX, dy: backgroundFrame.minY))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(theme: PresentationTheme, strings: PresentationStrings, code: String, number: String, sourceController: AuthorizationSequencePhoneEntryController) {
|
||||||
|
self.theme = theme
|
||||||
|
self.strings = strings
|
||||||
|
self.code = code
|
||||||
|
self.number = number
|
||||||
|
self.sourceController = sourceController
|
||||||
|
|
||||||
|
super.init(navigationBarPresentationData: nil)
|
||||||
|
|
||||||
|
self.blocksBackgroundWhenInOverlay = true
|
||||||
|
|
||||||
|
self.statusBar.statusBarStyle = .Ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
required public init(coder aDecoder: NSCoder) {
|
||||||
|
fatalError("init(coder:) has not been implemented")
|
||||||
|
}
|
||||||
|
|
||||||
|
private var isDismissed = false
|
||||||
|
override public func loadDisplayNode() {
|
||||||
|
self.displayNode = Node(theme: self.theme, strings: self.strings, code: self.code, number: self.number)
|
||||||
|
self.displayNodeDidLoad()
|
||||||
|
|
||||||
|
self.controllerNode.proceed = { [weak self] in
|
||||||
|
self?.proceed()
|
||||||
|
}
|
||||||
|
self.controllerNode.cancel = { [weak self] in
|
||||||
|
if let strongSelf = self, let sourceController = strongSelf.sourceController {
|
||||||
|
strongSelf.controllerNode.animateOut(codeNode: sourceController.codeNode, numberNode: sourceController.numberNode, buttonNode: sourceController.buttonNode, completion: { [weak self] in
|
||||||
|
self?.dismiss()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func transitionOut() {
|
||||||
|
self.controllerNode.cancel()
|
||||||
|
|
||||||
|
let transition = ContainedViewLayoutTransition.animated(duration: 0.5, curve: .spring)
|
||||||
|
transition.updatePosition(layer: self.view.layer, position: CGPoint(x: self.view.center.x - self.view.frame.width, y: self.view.center.y))
|
||||||
|
}
|
||||||
|
|
||||||
|
private var didPlayAppearanceAnimation = false
|
||||||
|
override public func viewDidAppear(_ animated: Bool) {
|
||||||
|
if !self.didPlayAppearanceAnimation {
|
||||||
|
self.didPlayAppearanceAnimation = true
|
||||||
|
if let sourceController = self.sourceController {
|
||||||
|
self.controllerNode.animateIn(codeNode: sourceController.codeNode, numberNode: sourceController.numberNode, buttonNode: sourceController.buttonNode)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override public func containerLayoutUpdated(_ layout: ContainerViewLayout, transition: ContainedViewLayoutTransition) {
|
||||||
|
super.containerLayoutUpdated(layout, transition: transition)
|
||||||
|
|
||||||
|
self.controllerNode.containerLayoutUpdated(layout, transition: transition)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private enum ManagedPhoneAnimationState: CaseIterable, Equatable {
|
||||||
|
case keypad1
|
||||||
|
case keypad2
|
||||||
|
case keypad3
|
||||||
|
case keypad4
|
||||||
|
}
|
||||||
|
|
||||||
|
private final class ManagedPhoneAnimationNode: ManagedAnimationNode {
|
||||||
|
private var phoneState: ManagedPhoneAnimationState = .keypad1
|
||||||
|
private var timer: SwiftSignalKit.Timer?
|
||||||
|
|
||||||
|
init() {
|
||||||
|
super.init(size: CGSize(width: 100.0, height: 100.0))
|
||||||
|
|
||||||
|
self.trackTo(item: ManagedAnimationItem(source: .local("IntroPhone"), frames: .range(startFrame: 0, endFrame: 0), duration: 0.3))
|
||||||
|
}
|
||||||
|
|
||||||
|
func enqueue(_ state: ManagedPhoneAnimationState) {
|
||||||
|
switch state {
|
||||||
|
case .keypad1:
|
||||||
|
self.trackTo(item: ManagedAnimationItem(source: .local("IntroPhone"), frames: .range(startFrame: 0, endFrame: 10), duration: 0.15))
|
||||||
|
self.trackTo(item: ManagedAnimationItem(source: .local("IntroPhone"), frames: .range(startFrame: 10, endFrame: 0), duration: 0.15))
|
||||||
|
case .keypad2:
|
||||||
|
self.trackTo(item: ManagedAnimationItem(source: .local("IntroPhone"), frames: .range(startFrame: 24, endFrame: 34), duration: 0.15))
|
||||||
|
self.trackTo(item: ManagedAnimationItem(source: .local("IntroPhone"), frames: .range(startFrame: 34, endFrame: 24), duration: 0.15))
|
||||||
|
self.trackTo(item: ManagedAnimationItem(source: .local("IntroPhone"), frames: .range(startFrame: 0, endFrame: 0), duration: 0.1))
|
||||||
|
case .keypad3:
|
||||||
|
self.trackTo(item: ManagedAnimationItem(source: .local("IntroPhone"), frames: .range(startFrame: 36, endFrame: 46), duration: 0.15))
|
||||||
|
self.trackTo(item: ManagedAnimationItem(source: .local("IntroPhone"), frames: .range(startFrame: 46, endFrame: 36), duration: 0.15))
|
||||||
|
self.trackTo(item: ManagedAnimationItem(source: .local("IntroPhone"), frames: .range(startFrame: 0, endFrame: 0), duration: 0.1))
|
||||||
|
case .keypad4:
|
||||||
|
self.trackTo(item: ManagedAnimationItem(source: .local("IntroPhone"), frames: .range(startFrame: 0, endFrame: 10), duration: 0.15))
|
||||||
|
self.trackTo(item: ManagedAnimationItem(source: .local("IntroPhone"), frames: .range(startFrame: 10, endFrame: 0), duration: 0.15))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -5,8 +5,8 @@ import Display
|
|||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
import TextFormat
|
import TextFormat
|
||||||
import Markdown
|
import Markdown
|
||||||
import AuthorizationUI
|
|
||||||
import SolidRoundedButtonNode
|
import SolidRoundedButtonNode
|
||||||
|
import AuthorizationUtils
|
||||||
|
|
||||||
private func roundCorners(diameter: CGFloat) -> UIImage {
|
private func roundCorners(diameter: CGFloat) -> UIImage {
|
||||||
UIGraphicsBeginImageContextWithOptions(CGSize(width: diameter, height: diameter), false, 0.0)
|
UIGraphicsBeginImageContextWithOptions(CGSize(width: diameter, height: diameter), false, 0.0)
|
@ -116,6 +116,10 @@ final class AuthorizationSequenceSplashController: ViewController {
|
|||||||
return self.startButton.frame
|
return self.startButton.frame
|
||||||
}
|
}
|
||||||
|
|
||||||
|
var buttonTitle: String {
|
||||||
|
return self.startButton.title ?? ""
|
||||||
|
}
|
||||||
|
|
||||||
var animationSnapshot: UIView? {
|
var animationSnapshot: UIView? {
|
||||||
return self.controller.createAnimationSnapshot()
|
return self.controller.createAnimationSnapshot()
|
||||||
}
|
}
|
23
submodules/AuthorizationUtils/BUILD
Normal file
23
submodules/AuthorizationUtils/BUILD
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
load("@build_bazel_rules_swift//swift:swift.bzl", "swift_library")
|
||||||
|
|
||||||
|
swift_library(
|
||||||
|
name = "AuthorizationUtils",
|
||||||
|
module_name = "AuthorizationUtils",
|
||||||
|
srcs = glob([
|
||||||
|
"Sources/**/*.swift",
|
||||||
|
]),
|
||||||
|
copts = [
|
||||||
|
"-warnings-as-errors",
|
||||||
|
],
|
||||||
|
deps = [
|
||||||
|
"//submodules/AsyncDisplayKit:AsyncDisplayKit",
|
||||||
|
"//submodules/TelegramCore:TelegramCore",
|
||||||
|
"//submodules/Display:Display",
|
||||||
|
"//submodules/TextFormat:TextFormat",
|
||||||
|
"//submodules/Markdown:Markdown",
|
||||||
|
"//submodules/TelegramPresentationData:TelegramPresentationData",
|
||||||
|
],
|
||||||
|
visibility = [
|
||||||
|
"//visibility:public",
|
||||||
|
],
|
||||||
|
)
|
@ -29,10 +29,17 @@ public func authorizationCurrentOptionText(_ type: SentAuthorizationCodeType, ph
|
|||||||
return NSAttributedString(string: "", font: Font.regular(fontSize), textColor: primaryColor, paragraphAlignment: .center)
|
return NSAttributedString(string: "", font: Font.regular(fontSize), textColor: primaryColor, paragraphAlignment: .center)
|
||||||
case let .email(emailPattern, _, _, _, _):
|
case let .email(emailPattern, _, _, _, _):
|
||||||
let mutableString = NSAttributedString(string: strings.Login_EnterCodeEmailText(email ?? emailPattern).string, font: Font.regular(fontSize), textColor: primaryColor, paragraphAlignment: .center).mutableCopy() as! NSMutableAttributedString
|
let mutableString = NSAttributedString(string: strings.Login_EnterCodeEmailText(email ?? emailPattern).string, font: Font.regular(fontSize), textColor: primaryColor, paragraphAlignment: .center).mutableCopy() as! NSMutableAttributedString
|
||||||
let range = (mutableString.string as NSString).range(of: "*******")
|
|
||||||
if range.location != NSNotFound {
|
let string = mutableString.string
|
||||||
mutableString.addAttribute(NSAttributedString.Key(rawValue: TelegramTextAttributes.Spoiler), value: true, range: range)
|
let nsString = string as NSString
|
||||||
|
|
||||||
|
if let regex = try? NSRegularExpression(pattern: "\\*", options: []) {
|
||||||
|
let matches = regex.matches(in: string, options: [], range: NSMakeRange(0, nsString.length))
|
||||||
|
if let first = matches.first {
|
||||||
|
mutableString.addAttribute(NSAttributedString.Key(rawValue: TelegramTextAttributes.Spoiler), value: true, range: NSRange(location: first.range.location, length: matches.count))
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return mutableString
|
return mutableString
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1696,7 +1696,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||||||
}, openPeer: { _, _ in
|
}, openPeer: { _, _ in
|
||||||
}, callPeer: { _, _ in
|
}, callPeer: { _, _ in
|
||||||
}, enqueueMessage: { _ in
|
}, enqueueMessage: { _ in
|
||||||
}, sendSticker: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in }, gallerySource: .custom(messages: foundMessages |> map { message, a, b in
|
}, sendSticker: nil, sendEmoji: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in }, gallerySource: .custom(messages: foundMessages |> map { message, a, b in
|
||||||
return (message.map { $0._asMessage() }, a, b)
|
return (message.map { $0._asMessage() }, a, b)
|
||||||
}, messageId: message.id, loadMore: {
|
}, messageId: message.id, loadMore: {
|
||||||
loadMore()
|
loadMore()
|
||||||
@ -1815,7 +1815,7 @@ final class ChatListSearchListPaneNode: ASDisplayNode, ChatListSearchPaneNode {
|
|||||||
}, openPeer: { peer, navigation in
|
}, openPeer: { peer, navigation in
|
||||||
}, callPeer: { _, _ in
|
}, callPeer: { _, _ in
|
||||||
}, enqueueMessage: { _ in
|
}, enqueueMessage: { _ in
|
||||||
}, sendSticker: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in }, playlistLocation: playlistLocation, gallerySource: gallerySource))
|
}, sendSticker: nil, sendEmoji: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in }, playlistLocation: playlistLocation, gallerySource: gallerySource))
|
||||||
}, openMessageContextMenu: { [weak self] message, _, node, rect, gesture in
|
}, openMessageContextMenu: { [weak self] message, _, node, rect, gesture in
|
||||||
guard let strongSelf = self, let currentEntries = strongSelf.currentEntries else {
|
guard let strongSelf = self, let currentEntries = strongSelf.currentEntries else {
|
||||||
return
|
return
|
||||||
|
@ -215,7 +215,7 @@ open class NavigationController: UINavigationController, ContainableController,
|
|||||||
return self._displayNode!
|
return self._displayNode!
|
||||||
}
|
}
|
||||||
|
|
||||||
var statusBarHost: StatusBarHost? {
|
public var statusBarHost: StatusBarHost? {
|
||||||
didSet {
|
didSet {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -70,6 +70,11 @@ public final class InAppPurchaseManager: NSObject {
|
|||||||
return self.numberFormatter.string(from: price) ?? ""
|
return self.numberFormatter.string(from: price) ?? ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func defaultPrice(_ value: NSDecimalNumber, monthsCount: Int) -> String {
|
||||||
|
let price = value.multiplying(by: NSDecimalNumber(value: monthsCount)).round(2)
|
||||||
|
return self.numberFormatter.string(from: price) ?? ""
|
||||||
|
}
|
||||||
|
|
||||||
public var priceValue: NSDecimalNumber {
|
public var priceValue: NSDecimalNumber {
|
||||||
return self.skProduct.price
|
return self.skProduct.price
|
||||||
}
|
}
|
||||||
|
@ -124,7 +124,7 @@ public func parseMarkdownIntoAttributedString(_ string: String, attributes: Mark
|
|||||||
|
|
||||||
if let bold = parseBold(string: nsString, remainingRange: &remainingRange) {
|
if let bold = parseBold(string: nsString, remainingRange: &remainingRange) {
|
||||||
var boldAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.font: attributes.bold.font, NSAttributedString.Key.foregroundColor: attributes.bold.textColor, NSAttributedString.Key.paragraphStyle: paragraphStyleWithAlignment(textAlignment)]
|
var boldAttributes: [NSAttributedString.Key: Any] = [NSAttributedString.Key.font: attributes.bold.font, NSAttributedString.Key.foregroundColor: attributes.bold.textColor, NSAttributedString.Key.paragraphStyle: paragraphStyleWithAlignment(textAlignment)]
|
||||||
if !attributes.body.additionalAttributes.isEmpty {
|
if !attributes.bold.additionalAttributes.isEmpty {
|
||||||
for (key, value) in attributes.bold.additionalAttributes {
|
for (key, value) in attributes.bold.additionalAttributes {
|
||||||
boldAttributes[NSAttributedString.Key(rawValue: key)] = value
|
boldAttributes[NSAttributedString.Key(rawValue: key)] = value
|
||||||
}
|
}
|
||||||
|
@ -132,6 +132,10 @@ public final class PhoneInputNode: ASDisplayNode, UITextFieldDelegate {
|
|||||||
|
|
||||||
private var countryNameForCode: (Int32, String)?
|
private var countryNameForCode: (Int32, String)?
|
||||||
|
|
||||||
|
public var formattedCodeAndNumber: (String, String) {
|
||||||
|
return (self.countryCodeField.textField.text ?? "", self.numberField.textField.text ?? "")
|
||||||
|
}
|
||||||
|
|
||||||
public var codeAndNumber: (Int32?, String?, String) {
|
public var codeAndNumber: (Int32?, String?, String) {
|
||||||
get {
|
get {
|
||||||
var code: Int32?
|
var code: Int32?
|
||||||
|
@ -58,41 +58,25 @@ class EmojiHeaderComponent: Component {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
private var _ready = Promise<Bool>()
|
private var _ready = Promise<Bool>(true)
|
||||||
var ready: Signal<Bool, NoError> {
|
var ready: Signal<Bool, NoError> {
|
||||||
return self._ready.get()
|
return self._ready.get()
|
||||||
}
|
}
|
||||||
|
|
||||||
weak var animateFrom: UIView?
|
weak var animateFrom: UIView?
|
||||||
weak var containerView: UIView?
|
weak var containerView: UIView?
|
||||||
var animationColor: UIColor?
|
|
||||||
|
|
||||||
private let sceneView: SCNView
|
|
||||||
let statusView: ComponentHostView<Empty>
|
let statusView: ComponentHostView<Empty>
|
||||||
|
|
||||||
private var previousInteractionTimestamp: Double = 0.0
|
|
||||||
private var timer: SwiftSignalKit.Timer?
|
|
||||||
private var hasIdleAnimations = false
|
private var hasIdleAnimations = false
|
||||||
|
|
||||||
override init(frame: CGRect) {
|
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<Empty>()
|
self.statusView = ComponentHostView<Empty>()
|
||||||
|
|
||||||
super.init(frame: frame)
|
super.init(frame: frame)
|
||||||
|
|
||||||
self.addSubview(self.sceneView)
|
|
||||||
self.addSubview(self.statusView)
|
self.addSubview(self.statusView)
|
||||||
|
|
||||||
self.setup()
|
|
||||||
|
|
||||||
let tapGestureRecoginzer = UITapGestureRecognizer(target: self, action: #selector(self.handleTap(_:)))
|
|
||||||
self.addGestureRecognizer(tapGestureRecoginzer)
|
|
||||||
|
|
||||||
self.disablesInteractiveModalDismiss = true
|
self.disablesInteractiveModalDismiss = true
|
||||||
self.disablesInteractiveTransitionGestureRecognizer = true
|
self.disablesInteractiveTransitionGestureRecognizer = true
|
||||||
}
|
}
|
||||||
@ -101,41 +85,7 @@ class EmojiHeaderComponent: Component {
|
|||||||
fatalError("init(coder:) has not been implemented")
|
fatalError("init(coder:) has not been implemented")
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
func animateIn() {
|
||||||
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 {
|
guard let animateFrom = self.animateFrom, var containerView = self.containerView else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -150,8 +100,8 @@ class EmojiHeaderComponent: Component {
|
|||||||
self.statusView.center = targetPosition
|
self.statusView.center = targetPosition
|
||||||
|
|
||||||
animateFrom.alpha = 0.0
|
animateFrom.alpha = 0.0
|
||||||
self.statusView.layer.animateScale(from: 0.05, to: 1.0, duration: 0.8, timingFunction: kCAMediaTimingFunctionSpring)
|
self.statusView.layer.animateScale(from: 0.05, to: 1.0, duration: 0.55, timingFunction: kCAMediaTimingFunctionSpring)
|
||||||
self.statusView.layer.animatePosition(from: sourcePosition, to: targetPosition, duration: 0.8, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in
|
self.statusView.layer.animatePosition(from: sourcePosition, to: targetPosition, duration: 0.55, timingFunction: kCAMediaTimingFunctionSpring, completion: { _ in
|
||||||
self.addSubview(self.statusView)
|
self.addSubview(self.statusView)
|
||||||
self.statusView.center = initialPosition
|
self.statusView.center = initialPosition
|
||||||
})
|
})
|
||||||
@ -164,129 +114,7 @@ class EmojiHeaderComponent: Component {
|
|||||||
self.containerView = 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 {
|
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
|
self.hasIdleAnimations = component.hasIdleAnimations
|
||||||
|
|
||||||
let size = self.statusView.update(
|
let size = self.statusView.update(
|
||||||
|
@ -257,13 +257,16 @@ private final class PremiumGiftScreenContentComponent: CombinedComponent {
|
|||||||
discount = ""
|
discount = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let pricePerMonth = product.storeProduct.pricePerMonth(Int(product.months))
|
||||||
|
|
||||||
items.append(SectionGroupComponent.Item(
|
items.append(SectionGroupComponent.Item(
|
||||||
AnyComponentWithIdentity(
|
AnyComponentWithIdentity(
|
||||||
id: product.id,
|
id: product.id,
|
||||||
component: AnyComponent(
|
component: AnyComponent(
|
||||||
PremiumOptionComponent(
|
PremiumOptionComponent(
|
||||||
title: giftTitle,
|
title: giftTitle,
|
||||||
totalPrice: product.price,
|
subtitle: product.price,
|
||||||
|
labelPrice: pricePerMonth,
|
||||||
discount: discount,
|
discount: discount,
|
||||||
selected: product.id == component.selectedProductId,
|
selected: product.id == component.selectedProductId,
|
||||||
primaryTextColor: textColor,
|
primaryTextColor: textColor,
|
||||||
|
@ -307,7 +307,8 @@ struct PremiumIntroConfiguration {
|
|||||||
|
|
||||||
final class PremiumOptionComponent: CombinedComponent {
|
final class PremiumOptionComponent: CombinedComponent {
|
||||||
let title: String
|
let title: String
|
||||||
let totalPrice: String
|
let subtitle: String
|
||||||
|
let labelPrice: String
|
||||||
let discount: String
|
let discount: String
|
||||||
let selected: Bool
|
let selected: Bool
|
||||||
let primaryTextColor: UIColor
|
let primaryTextColor: UIColor
|
||||||
@ -318,7 +319,8 @@ final class PremiumOptionComponent: CombinedComponent {
|
|||||||
|
|
||||||
init(
|
init(
|
||||||
title: String,
|
title: String,
|
||||||
totalPrice: String,
|
subtitle: String,
|
||||||
|
labelPrice: String,
|
||||||
discount: String,
|
discount: String,
|
||||||
selected: Bool,
|
selected: Bool,
|
||||||
primaryTextColor: UIColor,
|
primaryTextColor: UIColor,
|
||||||
@ -328,7 +330,8 @@ final class PremiumOptionComponent: CombinedComponent {
|
|||||||
checkBorderColor: UIColor
|
checkBorderColor: UIColor
|
||||||
) {
|
) {
|
||||||
self.title = title
|
self.title = title
|
||||||
self.totalPrice = totalPrice
|
self.subtitle = subtitle
|
||||||
|
self.labelPrice = labelPrice
|
||||||
self.discount = discount
|
self.discount = discount
|
||||||
self.selected = selected
|
self.selected = selected
|
||||||
self.primaryTextColor = primaryTextColor
|
self.primaryTextColor = primaryTextColor
|
||||||
@ -342,7 +345,10 @@ final class PremiumOptionComponent: CombinedComponent {
|
|||||||
if lhs.title != rhs.title {
|
if lhs.title != rhs.title {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if lhs.totalPrice != rhs.totalPrice {
|
if lhs.subtitle != rhs.subtitle {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.labelPrice != rhs.labelPrice {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
if lhs.discount != rhs.discount {
|
if lhs.discount != rhs.discount {
|
||||||
@ -372,6 +378,7 @@ final class PremiumOptionComponent: CombinedComponent {
|
|||||||
static var body: Body {
|
static var body: Body {
|
||||||
let check = Child(CheckComponent.self)
|
let check = Child(CheckComponent.self)
|
||||||
let title = Child(MultilineTextComponent.self)
|
let title = Child(MultilineTextComponent.self)
|
||||||
|
let subtitle = Child(MultilineTextComponent.self)
|
||||||
let discountBackground = Child(RoundedRectangle.self)
|
let discountBackground = Child(RoundedRectangle.self)
|
||||||
let discount = Child(MultilineTextComponent.self)
|
let discount = Child(MultilineTextComponent.self)
|
||||||
let label = Child(MultilineTextComponent.self)
|
let label = Child(MultilineTextComponent.self)
|
||||||
@ -379,13 +386,13 @@ final class PremiumOptionComponent: CombinedComponent {
|
|||||||
return { context in
|
return { context in
|
||||||
let component = context.component
|
let component = context.component
|
||||||
|
|
||||||
let insets = UIEdgeInsets(top: 11.0, left: 46.0, bottom: 13.0, right: 16.0)
|
var insets = UIEdgeInsets(top: 11.0, left: 46.0, bottom: 13.0, right: 16.0)
|
||||||
|
|
||||||
let label = label.update(
|
let label = label.update(
|
||||||
component: MultilineTextComponent(
|
component: MultilineTextComponent(
|
||||||
text: .plain(
|
text: .plain(
|
||||||
NSAttributedString(
|
NSAttributedString(
|
||||||
string: component.totalPrice,
|
string: component.labelPrice,
|
||||||
font: Font.regular(17),
|
font: Font.regular(17),
|
||||||
textColor: component.secondaryTextColor
|
textColor: component.secondaryTextColor
|
||||||
)
|
)
|
||||||
@ -411,6 +418,41 @@ final class PremiumOptionComponent: CombinedComponent {
|
|||||||
transition: context.transition
|
transition: context.transition
|
||||||
)
|
)
|
||||||
|
|
||||||
|
var spacing: CGFloat = 0.0
|
||||||
|
var subtitleHeight: CGFloat = 0.0
|
||||||
|
if !component.subtitle.isEmpty {
|
||||||
|
spacing = 2.0
|
||||||
|
|
||||||
|
let subtitleFont = Font.regular(13)
|
||||||
|
let subtitleColor = component.secondaryTextColor
|
||||||
|
|
||||||
|
let subtitleString = parseMarkdownIntoAttributedString(
|
||||||
|
component.subtitle,
|
||||||
|
attributes: MarkdownAttributes(
|
||||||
|
body: MarkdownAttributeSet(font: subtitleFont, textColor: subtitleColor),
|
||||||
|
bold: MarkdownAttributeSet(font: subtitleFont, textColor: subtitleColor, additionalAttributes: [NSAttributedString.Key.strikethroughStyle.rawValue: NSUnderlineStyle.single.rawValue as NSNumber]),
|
||||||
|
link: MarkdownAttributeSet(font: subtitleFont, textColor: subtitleColor),
|
||||||
|
linkAttribute: { _ in return nil }
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
|
let subtitle = subtitle.update(
|
||||||
|
component: MultilineTextComponent(
|
||||||
|
text: .plain(subtitleString),
|
||||||
|
maximumNumberOfLines: 1
|
||||||
|
),
|
||||||
|
availableSize: CGSize(width: context.availableSize.width - insets.left - insets.right - label.size.width, height: context.availableSize.height),
|
||||||
|
transition: context.transition
|
||||||
|
)
|
||||||
|
context.add(subtitle
|
||||||
|
.position(CGPoint(x: insets.left + subtitle.size.width / 2.0, y: insets.top + title.size.height + spacing + subtitle.size.height / 2.0))
|
||||||
|
)
|
||||||
|
subtitleHeight = subtitle.size.height
|
||||||
|
|
||||||
|
insets.top -= 2.0
|
||||||
|
insets.bottom -= 2.0
|
||||||
|
}
|
||||||
|
|
||||||
let discountSize: CGSize
|
let discountSize: CGSize
|
||||||
if !component.discount.isEmpty {
|
if !component.discount.isEmpty {
|
||||||
let discount = discount.update(
|
let discount = discount.update(
|
||||||
@ -470,7 +512,7 @@ final class PremiumOptionComponent: CombinedComponent {
|
|||||||
.position(CGPoint(x: insets.left + title.size.width / 2.0, y: insets.top + title.size.height / 2.0))
|
.position(CGPoint(x: insets.left + title.size.width / 2.0, y: insets.top + title.size.height / 2.0))
|
||||||
)
|
)
|
||||||
|
|
||||||
let size = CGSize(width: context.availableSize.width, height: insets.top + title.size.height + insets.bottom)
|
let size = CGSize(width: context.availableSize.width, height: insets.top + title.size.height + spacing + subtitleHeight + insets.bottom)
|
||||||
|
|
||||||
context.add(label
|
context.add(label
|
||||||
.position(CGPoint(x: context.availableSize.width - insets.right - label.size.width / 2.0, y: size.height / 2.0))
|
.position(CGPoint(x: context.availableSize.width - insets.right - label.size.width / 2.0, y: size.height / 2.0))
|
||||||
@ -1198,6 +1240,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
let buy = context.component.buy
|
let buy = context.component.buy
|
||||||
let updateIsFocused = context.component.updateIsFocused
|
let updateIsFocused = context.component.updateIsFocused
|
||||||
|
|
||||||
|
let layoutOptions = {
|
||||||
if state.isPremium == true {
|
if state.isPremium == true {
|
||||||
|
|
||||||
} else if let products = state.products {
|
} else if let products = state.products {
|
||||||
@ -1208,11 +1251,11 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
UIColor(rgb: 0xb36eee)
|
UIColor(rgb: 0xb36eee)
|
||||||
]
|
]
|
||||||
|
|
||||||
let shortestOptionPrice: Int64
|
let shortestOptionPrice: (Int64, NSDecimalNumber)
|
||||||
if let product = products.first(where: { $0.id.hasSuffix(".monthly") }) {
|
if let product = products.first(where: { $0.id.hasSuffix(".monthly") }) {
|
||||||
shortestOptionPrice = Int64(Float(product.priceCurrencyAndAmount.amount))
|
shortestOptionPrice = (Int64(Float(product.priceCurrencyAndAmount.amount)), product.priceValue)
|
||||||
} else {
|
} else {
|
||||||
shortestOptionPrice = 1
|
shortestOptionPrice = (1, NSDecimalNumber(decimal: 1))
|
||||||
}
|
}
|
||||||
|
|
||||||
var i = 0
|
var i = 0
|
||||||
@ -1227,7 +1270,7 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
months = 12
|
months = 12
|
||||||
}
|
}
|
||||||
|
|
||||||
let discountValue = Int((1.0 - Float(product.priceCurrencyAndAmount.amount) / months / Float(shortestOptionPrice)) * 100.0)
|
let discountValue = Int((1.0 - Float(product.priceCurrencyAndAmount.amount) / months / Float(shortestOptionPrice.0)) * 100.0)
|
||||||
let discount: String
|
let discount: String
|
||||||
if discountValue > 0 {
|
if discountValue > 0 {
|
||||||
discount = "-\(discountValue)%"
|
discount = "-\(discountValue)%"
|
||||||
@ -1235,6 +1278,24 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
discount = ""
|
discount = ""
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let defaultPrice = product.defaultPrice(shortestOptionPrice.1, monthsCount: Int(months))
|
||||||
|
|
||||||
|
var subtitle = ""
|
||||||
|
var pricePerMonth = product.price
|
||||||
|
if months > 1 {
|
||||||
|
pricePerMonth = product.pricePerMonth(Int(months))
|
||||||
|
|
||||||
|
if defaultPrice != product.price {
|
||||||
|
subtitle = "**\(defaultPrice)** \(product.price)"
|
||||||
|
if months == 12 {
|
||||||
|
subtitle = environment.strings.Premium_PricePerYear(subtitle).string
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
subtitle = product.price
|
||||||
|
}
|
||||||
|
}
|
||||||
|
pricePerMonth = environment.strings.Premium_PricePerMonth(pricePerMonth).string
|
||||||
|
|
||||||
optionsItems.append(
|
optionsItems.append(
|
||||||
SectionGroupComponent.Item(
|
SectionGroupComponent.Item(
|
||||||
AnyComponentWithIdentity(
|
AnyComponentWithIdentity(
|
||||||
@ -1242,7 +1303,8 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
component: AnyComponent(
|
component: AnyComponent(
|
||||||
PremiumOptionComponent(
|
PremiumOptionComponent(
|
||||||
title: giftTitle,
|
title: giftTitle,
|
||||||
totalPrice: product.price,
|
subtitle: subtitle,
|
||||||
|
labelPrice: pricePerMonth,
|
||||||
discount: discount,
|
discount: discount,
|
||||||
selected: product.id == state.selectedProductId,
|
selected: product.id == state.selectedProductId,
|
||||||
primaryTextColor: textColor,
|
primaryTextColor: textColor,
|
||||||
@ -1278,9 +1340,16 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
.cornerRadius(10.0)
|
.cornerRadius(10.0)
|
||||||
)
|
)
|
||||||
size.height += optionsSection.size.height
|
size.height += optionsSection.size.height
|
||||||
|
|
||||||
|
if case .emojiStatus = context.component.source {
|
||||||
|
size.height -= 18.0
|
||||||
|
} else {
|
||||||
size.height += 26.0
|
size.height += 26.0
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
let layoutPerks = {
|
||||||
var i = 0
|
var i = 0
|
||||||
var perksItems: [SectionGroupComponent.Item] = []
|
var perksItems: [SectionGroupComponent.Item] = []
|
||||||
for perk in state.configuration.perks {
|
for perk in state.configuration.perks {
|
||||||
@ -1389,7 +1458,24 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
.cornerRadius(10.0)
|
.cornerRadius(10.0)
|
||||||
)
|
)
|
||||||
size.height += perksSection.size.height
|
size.height += perksSection.size.height
|
||||||
|
|
||||||
|
if case .emojiStatus = context.component.source {
|
||||||
|
if state.isPremium == true {
|
||||||
|
size.height -= 23.0
|
||||||
|
} else {
|
||||||
size.height += 23.0
|
size.height += 23.0
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
size.height += 23.0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if case .emojiStatus = context.component.source {
|
||||||
|
layoutPerks()
|
||||||
|
layoutOptions()
|
||||||
|
} else {
|
||||||
|
layoutOptions()
|
||||||
|
layoutPerks()
|
||||||
|
|
||||||
let textSideInset: CGFloat = 16.0
|
let textSideInset: CGFloat = 16.0
|
||||||
let textPadding: CGFloat = 13.0
|
let textPadding: CGFloat = 13.0
|
||||||
@ -1530,6 +1616,8 @@ private final class PremiumIntroScreenContentComponent: CombinedComponent {
|
|||||||
)
|
)
|
||||||
size.height += termsText.size.height
|
size.height += termsText.size.height
|
||||||
size.height += 10.0
|
size.height += 10.0
|
||||||
|
}
|
||||||
|
|
||||||
size.height += scrollEnvironment.insets.bottom
|
size.height += scrollEnvironment.insets.bottom
|
||||||
|
|
||||||
if context.component.source != .settings {
|
if context.component.source != .settings {
|
||||||
@ -1713,10 +1801,11 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
|||||||
otherPeerName
|
otherPeerName
|
||||||
).start(next: { [weak self] products, isPremium, otherPeerName in
|
).start(next: { [weak self] products, isPremium, otherPeerName in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
if strongSelf.products == nil {
|
let hadProducts = strongSelf.products != nil
|
||||||
strongSelf.selectedProductId = products.first?.id
|
|
||||||
}
|
|
||||||
strongSelf.products = products.filter { $0.isSubscription }
|
strongSelf.products = products.filter { $0.isSubscription }
|
||||||
|
if !hadProducts {
|
||||||
|
strongSelf.selectedProductId = strongSelf.products?.last?.id
|
||||||
|
}
|
||||||
strongSelf.isPremium = isPremium
|
strongSelf.isPremium = isPremium
|
||||||
strongSelf.otherPeerName = otherPeerName
|
strongSelf.otherPeerName = otherPeerName
|
||||||
strongSelf.updated(transition: .immediate)
|
strongSelf.updated(transition: .immediate)
|
||||||
@ -1786,7 +1875,6 @@ private final class PremiumIntroScreenComponent: CombinedComponent {
|
|||||||
strongSelf.updateInProgress(false)
|
strongSelf.updateInProgress(false)
|
||||||
|
|
||||||
strongSelf.updated(transition: .immediate)
|
strongSelf.updated(transition: .immediate)
|
||||||
strongSelf.completion()
|
|
||||||
|
|
||||||
addAppLogEvent(postbox: strongSelf.context.account.postbox, type: "premium.promo_screen_fail")
|
addAppLogEvent(postbox: strongSelf.context.account.postbox, type: "premium.promo_screen_fail")
|
||||||
|
|
||||||
@ -2366,7 +2454,10 @@ public final class PremiumIntroScreen: ViewControllerComponentContainer {
|
|||||||
if let sourceView = self.sourceView {
|
if let sourceView = self.sourceView {
|
||||||
view.animateFrom = sourceView
|
view.animateFrom = sourceView
|
||||||
view.containerView = self.containerView
|
view.containerView = self.containerView
|
||||||
view.animationColor = self.animationColor
|
|
||||||
|
Queue.mainQueue().after(0.1) {
|
||||||
|
view.animateIn()
|
||||||
|
}
|
||||||
|
|
||||||
self.sourceView = nil
|
self.sourceView = nil
|
||||||
self.containerView = nil
|
self.containerView = nil
|
||||||
|
@ -5,6 +5,6 @@ import TelegramPresentationData
|
|||||||
|
|
||||||
public extension SolidRoundedButtonTheme {
|
public extension SolidRoundedButtonTheme {
|
||||||
convenience init(theme: PresentationTheme) {
|
convenience init(theme: PresentationTheme) {
|
||||||
self.init(backgroundColor: theme.list.itemCheckColors.fillColor, backgroundColors: [], foregroundColor: theme.list.itemCheckColors.foregroundColor)
|
self.init(backgroundColor: theme.list.itemCheckColors.fillColor, backgroundColors: [], foregroundColor: theme.list.itemCheckColors.foregroundColor, disabledBackgroundColor: theme.list.plainBackgroundColor.mixedWith(theme.list.itemDisabledTextColor, alpha: 0.15), disabledForegroundColor: theme.list.itemDisabledTextColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -73,6 +73,7 @@ swift_library(
|
|||||||
"//submodules/HexColor:HexColor",
|
"//submodules/HexColor:HexColor",
|
||||||
"//submodules/QrCode:QrCode",
|
"//submodules/QrCode:QrCode",
|
||||||
"//submodules/WallpaperResources:WallpaperResources",
|
"//submodules/WallpaperResources:WallpaperResources",
|
||||||
|
"//submodules/AuthorizationUtils:AuthorizationUtils",
|
||||||
"//submodules/AuthorizationUI:AuthorizationUI",
|
"//submodules/AuthorizationUI:AuthorizationUI",
|
||||||
"//submodules/InstantPageUI:InstantPageUI",
|
"//submodules/InstantPageUI:InstantPageUI",
|
||||||
"//submodules/CheckNode:CheckNode",
|
"//submodules/CheckNode:CheckNode",
|
||||||
|
@ -11,7 +11,7 @@ import OverlayStatusController
|
|||||||
import AccountContext
|
import AccountContext
|
||||||
import AlertUI
|
import AlertUI
|
||||||
import PresentationDataUtils
|
import PresentationDataUtils
|
||||||
import AuthorizationUI
|
import AuthorizationUtils
|
||||||
import PhoneNumberFormat
|
import PhoneNumberFormat
|
||||||
|
|
||||||
private final class ChangePhoneNumberCodeControllerArguments {
|
private final class ChangePhoneNumberCodeControllerArguments {
|
||||||
|
@ -10,7 +10,7 @@ import PresentationDataUtils
|
|||||||
import AccountContext
|
import AccountContext
|
||||||
import AlertUI
|
import AlertUI
|
||||||
import PresentationDataUtils
|
import PresentationDataUtils
|
||||||
import AuthorizationUI
|
import AuthorizationUtils
|
||||||
import PhoneNumberFormat
|
import PhoneNumberFormat
|
||||||
|
|
||||||
private final class ConfirmPhoneNumberCodeControllerArguments {
|
private final class ConfirmPhoneNumberCodeControllerArguments {
|
||||||
|
@ -16,6 +16,7 @@ import AppBundle
|
|||||||
import PasswordSetupUI
|
import PasswordSetupUI
|
||||||
import UndoUI
|
import UndoUI
|
||||||
import PremiumUI
|
import PremiumUI
|
||||||
|
import AuthorizationUI
|
||||||
|
|
||||||
private final class PrivacyAndSecurityControllerArguments {
|
private final class PrivacyAndSecurityControllerArguments {
|
||||||
let account: Account
|
let account: Account
|
||||||
@ -33,8 +34,9 @@ private final class PrivacyAndSecurityControllerArguments {
|
|||||||
let toggleArchiveAndMuteNonContacts: (Bool) -> Void
|
let toggleArchiveAndMuteNonContacts: (Bool) -> Void
|
||||||
let setupAccountAutoremove: () -> Void
|
let setupAccountAutoremove: () -> Void
|
||||||
let openDataSettings: () -> Void
|
let openDataSettings: () -> Void
|
||||||
|
let openEmailSettings: (String?) -> Void
|
||||||
|
|
||||||
init(account: Account, openBlockedUsers: @escaping () -> Void, openLastSeenPrivacy: @escaping () -> Void, openGroupsPrivacy: @escaping () -> Void, openVoiceCallPrivacy: @escaping () -> Void, openProfilePhotoPrivacy: @escaping () -> Void, openForwardPrivacy: @escaping () -> Void, openPhoneNumberPrivacy: @escaping () -> Void, openVoiceMessagePrivacy: @escaping () -> Void, openPasscode: @escaping () -> Void, openTwoStepVerification: @escaping (TwoStepVerificationAccessConfiguration?) -> Void, openActiveSessions: @escaping () -> Void, toggleArchiveAndMuteNonContacts: @escaping (Bool) -> Void, setupAccountAutoremove: @escaping () -> Void, openDataSettings: @escaping () -> Void) {
|
init(account: Account, openBlockedUsers: @escaping () -> Void, openLastSeenPrivacy: @escaping () -> Void, openGroupsPrivacy: @escaping () -> Void, openVoiceCallPrivacy: @escaping () -> Void, openProfilePhotoPrivacy: @escaping () -> Void, openForwardPrivacy: @escaping () -> Void, openPhoneNumberPrivacy: @escaping () -> Void, openVoiceMessagePrivacy: @escaping () -> Void, openPasscode: @escaping () -> Void, openTwoStepVerification: @escaping (TwoStepVerificationAccessConfiguration?) -> Void, openActiveSessions: @escaping () -> Void, toggleArchiveAndMuteNonContacts: @escaping (Bool) -> Void, setupAccountAutoremove: @escaping () -> Void, openDataSettings: @escaping () -> Void, openEmailSettings: @escaping (String?) -> Void) {
|
||||||
self.account = account
|
self.account = account
|
||||||
self.openBlockedUsers = openBlockedUsers
|
self.openBlockedUsers = openBlockedUsers
|
||||||
self.openLastSeenPrivacy = openLastSeenPrivacy
|
self.openLastSeenPrivacy = openLastSeenPrivacy
|
||||||
@ -50,6 +52,7 @@ private final class PrivacyAndSecurityControllerArguments {
|
|||||||
self.toggleArchiveAndMuteNonContacts = toggleArchiveAndMuteNonContacts
|
self.toggleArchiveAndMuteNonContacts = toggleArchiveAndMuteNonContacts
|
||||||
self.setupAccountAutoremove = setupAccountAutoremove
|
self.setupAccountAutoremove = setupAccountAutoremove
|
||||||
self.openDataSettings = openDataSettings
|
self.openDataSettings = openDataSettings
|
||||||
|
self.openEmailSettings = openEmailSettings
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -87,6 +90,7 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry {
|
|||||||
case selectivePrivacyInfo(PresentationTheme, String)
|
case selectivePrivacyInfo(PresentationTheme, String)
|
||||||
case passcode(PresentationTheme, String, Bool, String)
|
case passcode(PresentationTheme, String, Bool, String)
|
||||||
case twoStepVerification(PresentationTheme, String, String, TwoStepVerificationAccessConfiguration?)
|
case twoStepVerification(PresentationTheme, String, String, TwoStepVerificationAccessConfiguration?)
|
||||||
|
case loginEmail(PresentationTheme, String, String?)
|
||||||
case activeSessions(PresentationTheme, String, String)
|
case activeSessions(PresentationTheme, String, String)
|
||||||
case autoArchiveHeader(String)
|
case autoArchiveHeader(String)
|
||||||
case autoArchive(String, Bool)
|
case autoArchive(String, Bool)
|
||||||
@ -99,7 +103,7 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry {
|
|||||||
|
|
||||||
var section: ItemListSectionId {
|
var section: ItemListSectionId {
|
||||||
switch self {
|
switch self {
|
||||||
case .blockedPeers, .activeSessions, .passcode, .twoStepVerification:
|
case .blockedPeers, .activeSessions, .passcode, .twoStepVerification, .loginEmail:
|
||||||
return PrivacyAndSecuritySection.general.rawValue
|
return PrivacyAndSecuritySection.general.rawValue
|
||||||
case .privacyHeader, .phoneNumberPrivacy, .lastSeenPrivacy, .profilePhotoPrivacy, .forwardPrivacy, .groupPrivacy, .voiceCallPrivacy, .voiceMessagePrivacy, .selectivePrivacyInfo:
|
case .privacyHeader, .phoneNumberPrivacy, .lastSeenPrivacy, .profilePhotoPrivacy, .forwardPrivacy, .groupPrivacy, .voiceCallPrivacy, .voiceMessagePrivacy, .selectivePrivacyInfo:
|
||||||
return PrivacyAndSecuritySection.privacy.rawValue
|
return PrivacyAndSecuritySection.privacy.rawValue
|
||||||
@ -122,40 +126,42 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry {
|
|||||||
return 3
|
return 3
|
||||||
case .twoStepVerification:
|
case .twoStepVerification:
|
||||||
return 4
|
return 4
|
||||||
case .privacyHeader:
|
case .loginEmail:
|
||||||
return 5
|
return 5
|
||||||
case .phoneNumberPrivacy:
|
case .privacyHeader:
|
||||||
return 6
|
return 6
|
||||||
case .lastSeenPrivacy:
|
case .phoneNumberPrivacy:
|
||||||
return 7
|
return 7
|
||||||
case .profilePhotoPrivacy:
|
case .lastSeenPrivacy:
|
||||||
return 8
|
return 8
|
||||||
case .voiceCallPrivacy:
|
case .profilePhotoPrivacy:
|
||||||
return 9
|
return 9
|
||||||
case .voiceMessagePrivacy:
|
case .voiceCallPrivacy:
|
||||||
return 10
|
return 10
|
||||||
case .forwardPrivacy:
|
case .voiceMessagePrivacy:
|
||||||
return 11
|
return 11
|
||||||
case .groupPrivacy:
|
case .forwardPrivacy:
|
||||||
return 12
|
return 12
|
||||||
case .selectivePrivacyInfo:
|
case .groupPrivacy:
|
||||||
return 13
|
return 13
|
||||||
case .autoArchiveHeader:
|
case .selectivePrivacyInfo:
|
||||||
return 14
|
return 14
|
||||||
case .autoArchive:
|
case .autoArchiveHeader:
|
||||||
return 15
|
return 15
|
||||||
case .autoArchiveInfo:
|
case .autoArchive:
|
||||||
return 16
|
return 16
|
||||||
case .accountHeader:
|
case .autoArchiveInfo:
|
||||||
return 17
|
return 17
|
||||||
case .accountTimeout:
|
case .accountHeader:
|
||||||
return 18
|
return 18
|
||||||
case .accountInfo:
|
case .accountTimeout:
|
||||||
return 19
|
return 19
|
||||||
case .dataSettings:
|
case .accountInfo:
|
||||||
return 20
|
return 20
|
||||||
case .dataSettingsInfo:
|
case .dataSettings:
|
||||||
return 21
|
return 21
|
||||||
|
case .dataSettingsInfo:
|
||||||
|
return 22
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -233,6 +239,12 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry {
|
|||||||
} else {
|
} else {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
case let .loginEmail(lhsTheme, lhsText, lhsEmailPattern):
|
||||||
|
if case let .loginEmail(rhsTheme, rhsText, rhsEmailPattern) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsEmailPattern == rhsEmailPattern {
|
||||||
|
return true
|
||||||
|
} else {
|
||||||
|
return false
|
||||||
|
}
|
||||||
case let .activeSessions(lhsTheme, lhsText, lhsValue):
|
case let .activeSessions(lhsTheme, lhsText, lhsValue):
|
||||||
if case let .activeSessions(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
if case let .activeSessions(rhsTheme, rhsText, rhsValue) = rhs, lhsTheme === rhsTheme, lhsText == rhsText, lhsValue == rhsValue {
|
||||||
return true
|
return true
|
||||||
@ -341,6 +353,10 @@ private enum PrivacyAndSecurityEntry: ItemListNodeEntry {
|
|||||||
return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/Menu/TwoStepAuth")?.precomposed(), title: text, label: value, sectionId: self.section, style: .blocks, action: {
|
return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/Menu/TwoStepAuth")?.precomposed(), title: text, label: value, sectionId: self.section, style: .blocks, action: {
|
||||||
arguments.openTwoStepVerification(data)
|
arguments.openTwoStepVerification(data)
|
||||||
})
|
})
|
||||||
|
case let .loginEmail(_, text, emailPattern):
|
||||||
|
return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/Menu/LoginEmail")?.precomposed(), title: text, label: "", sectionId: self.section, style: .blocks, action: {
|
||||||
|
arguments.openEmailSettings(emailPattern)
|
||||||
|
})
|
||||||
case let .activeSessions(_, text, value):
|
case let .activeSessions(_, text, value):
|
||||||
return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/Menu/Websites")?.precomposed(), title: text, label: value, sectionId: self.section, style: .blocks, action: {
|
return ItemListDisclosureItem(presentationData: presentationData, icon: UIImage(bundleImageName: "Settings/Menu/Websites")?.precomposed(), title: text, label: value, sectionId: self.section, style: .blocks, action: {
|
||||||
arguments.openActiveSessions()
|
arguments.openActiveSessions()
|
||||||
@ -411,7 +427,20 @@ private func stringForSelectiveSettings(strings: PresentationStrings, settings:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func privacyAndSecurityControllerEntries(presentationData: PresentationData, state: PrivacyAndSecurityControllerState, privacySettings: AccountPrivacySettings?, accessChallengeData: PostboxAccessChallengeData, blockedPeerCount: Int?, activeWebsitesCount: Int, hasTwoStepAuth: Bool?, twoStepAuthData: TwoStepVerificationAccessConfiguration?, canAutoarchive: Bool, isPremiumDisabled: Bool, isPremium: Bool) -> [PrivacyAndSecurityEntry] {
|
private func privacyAndSecurityControllerEntries(
|
||||||
|
presentationData: PresentationData,
|
||||||
|
state: PrivacyAndSecurityControllerState,
|
||||||
|
privacySettings: AccountPrivacySettings?,
|
||||||
|
accessChallengeData: PostboxAccessChallengeData,
|
||||||
|
blockedPeerCount: Int?,
|
||||||
|
activeWebsitesCount: Int,
|
||||||
|
hasTwoStepAuth: Bool?,
|
||||||
|
twoStepAuthData: TwoStepVerificationAccessConfiguration?,
|
||||||
|
canAutoarchive: Bool,
|
||||||
|
isPremiumDisabled: Bool,
|
||||||
|
isPremium: Bool,
|
||||||
|
loginEmail: String?
|
||||||
|
) -> [PrivacyAndSecurityEntry] {
|
||||||
var entries: [PrivacyAndSecurityEntry] = []
|
var entries: [PrivacyAndSecurityEntry] = []
|
||||||
|
|
||||||
entries.append(.blockedPeers(presentationData.theme, presentationData.strings.Settings_BlockedUsers, blockedPeerCount == nil ? "" : (blockedPeerCount == 0 ? presentationData.strings.PrivacySettings_BlockedPeersEmpty : "\(blockedPeerCount!)")))
|
entries.append(.blockedPeers(presentationData.theme, presentationData.strings.Settings_BlockedUsers, blockedPeerCount == nil ? "" : (blockedPeerCount == 0 ? presentationData.strings.PrivacySettings_BlockedPeersEmpty : "\(blockedPeerCount!)")))
|
||||||
@ -443,6 +472,8 @@ private func privacyAndSecurityControllerEntries(presentationData: PresentationD
|
|||||||
}
|
}
|
||||||
entries.append(.twoStepVerification(presentationData.theme, presentationData.strings.PrivacySettings_TwoStepAuth, twoStepAuthString, twoStepAuthData))
|
entries.append(.twoStepVerification(presentationData.theme, presentationData.strings.PrivacySettings_TwoStepAuth, twoStepAuthString, twoStepAuthData))
|
||||||
|
|
||||||
|
entries.append(.loginEmail(presentationData.theme, presentationData.strings.PrivacySettings_LoginEmail, loginEmail))
|
||||||
|
|
||||||
entries.append(.privacyHeader(presentationData.theme, presentationData.strings.PrivacySettings_PrivacyTitle))
|
entries.append(.privacyHeader(presentationData.theme, presentationData.strings.PrivacySettings_PrivacyTitle))
|
||||||
if let privacySettings = privacySettings {
|
if let privacySettings = privacySettings {
|
||||||
entries.append(.phoneNumberPrivacy(presentationData.theme, presentationData.strings.PrivacySettings_PhoneNumber, stringForSelectiveSettings(strings: presentationData.strings, settings: privacySettings.phoneNumber)))
|
entries.append(.phoneNumberPrivacy(presentationData.theme, presentationData.strings.PrivacySettings_PhoneNumber, stringForSelectiveSettings(strings: presentationData.strings, settings: privacySettings.phoneNumber)))
|
||||||
@ -509,7 +540,7 @@ class PrivacyAndSecurityControllerImpl: ItemListController {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func privacyAndSecurityController(context: AccountContext, initialSettings: AccountPrivacySettings? = nil, updatedSettings: ((AccountPrivacySettings?) -> Void)? = nil, updatedBlockedPeers: ((BlockedPeersContext?) -> Void)? = nil, updatedHasTwoStepAuth: ((Bool) -> Void)? = nil, focusOnItemTag: PrivacyAndSecurityEntryTag? = nil, activeSessionsContext: ActiveSessionsContext? = nil, webSessionsContext: WebSessionsContext? = nil, blockedPeersContext: BlockedPeersContext? = nil, hasTwoStepAuth: Bool? = nil) -> ViewController {
|
public func privacyAndSecurityController(context: AccountContext, initialSettings: AccountPrivacySettings? = nil, updatedSettings: ((AccountPrivacySettings?) -> Void)? = nil, updatedBlockedPeers: ((BlockedPeersContext?) -> Void)? = nil, updatedHasTwoStepAuth: ((Bool) -> Void)? = nil, focusOnItemTag: PrivacyAndSecurityEntryTag? = nil, activeSessionsContext: ActiveSessionsContext? = nil, webSessionsContext: WebSessionsContext? = nil, blockedPeersContext: BlockedPeersContext? = nil, hasTwoStepAuth: Bool? = nil, loginEmailPattern: Signal<String?, NoError>? = nil) -> ViewController {
|
||||||
let statePromise = ValuePromise(PrivacyAndSecurityControllerState(), ignoreRepeated: true)
|
let statePromise = ValuePromise(PrivacyAndSecurityControllerState(), ignoreRepeated: true)
|
||||||
let stateValue = Atomic(value: PrivacyAndSecurityControllerState())
|
let stateValue = Atomic(value: PrivacyAndSecurityControllerState())
|
||||||
let updateState: ((PrivacyAndSecurityControllerState) -> PrivacyAndSecurityControllerState) -> Void = { f in
|
let updateState: ((PrivacyAndSecurityControllerState) -> PrivacyAndSecurityControllerState) -> Void = { f in
|
||||||
@ -568,6 +599,23 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
|
|||||||
twoStepAuth.set(hasTwoStepAuthDataValue)
|
twoStepAuth.set(hasTwoStepAuthDataValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let loginEmail: Signal<String?, NoError>
|
||||||
|
if let loginEmailPattern = loginEmailPattern {
|
||||||
|
loginEmail = loginEmailPattern
|
||||||
|
} else {
|
||||||
|
loginEmail = .single(nil)
|
||||||
|
|> then(
|
||||||
|
context.engine.auth.twoStepAuthData()
|
||||||
|
|> map(Optional.init)
|
||||||
|
|> `catch` { _ -> Signal<TwoStepAuthData?, NoError> in
|
||||||
|
return .single(nil)
|
||||||
|
}
|
||||||
|
|> map { data -> String? in
|
||||||
|
return data?.loginEmailPattern
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
let updateHasTwoStepAuth: () -> Void = {
|
let updateHasTwoStepAuth: () -> Void = {
|
||||||
let signal = context.engine.auth.twoStepVerificationConfiguration()
|
let signal = context.engine.auth.twoStepVerificationConfiguration()
|
||||||
|> map { value -> TwoStepVerificationAccessConfiguration? in
|
|> map { value -> TwoStepVerificationAccessConfiguration? in
|
||||||
@ -589,6 +637,8 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
|
|||||||
}
|
}
|
||||||
updateHasTwoStepAuth()
|
updateHasTwoStepAuth()
|
||||||
|
|
||||||
|
var setupEmailImpl: ((String?) -> Void)?
|
||||||
|
|
||||||
let arguments = PrivacyAndSecurityControllerArguments(account: context.account, openBlockedUsers: {
|
let arguments = PrivacyAndSecurityControllerArguments(account: context.account, openBlockedUsers: {
|
||||||
pushControllerImpl?(blockedPeersController(context: context, blockedPeersContext: blockedPeersContext), true)
|
pushControllerImpl?(blockedPeersController(context: context, blockedPeersContext: blockedPeersContext), true)
|
||||||
}, openLastSeenPrivacy: {
|
}, openLastSeenPrivacy: {
|
||||||
@ -926,6 +976,23 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
|
|||||||
}))
|
}))
|
||||||
}, openDataSettings: {
|
}, openDataSettings: {
|
||||||
pushControllerImpl?(dataPrivacyController(context: context), true)
|
pushControllerImpl?(dataPrivacyController(context: context), true)
|
||||||
|
}, openEmailSettings: { emailPattern in
|
||||||
|
if let emailPattern = emailPattern {
|
||||||
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
let controller = textAlertController(
|
||||||
|
context: context, title: emailPattern, text: "This email address will be used every time you login to your Telegram account from a new device.", actions: [
|
||||||
|
TextAlertAction(type: .genericAction, title: "Change Email", action: {
|
||||||
|
setupEmailImpl?(emailPattern)
|
||||||
|
}),
|
||||||
|
TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_Cancel, action: {
|
||||||
|
|
||||||
|
})
|
||||||
|
], actionLayout: .vertical
|
||||||
|
)
|
||||||
|
presentControllerImpl?(controller)
|
||||||
|
} else {
|
||||||
|
setupEmailImpl?(nil)
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
actionsDisposable.add(context.engine.peers.managedUpdatedRecentPeers().start())
|
actionsDisposable.add(context.engine.peers.managedUpdatedRecentPeers().start())
|
||||||
@ -953,9 +1020,10 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
|
|||||||
context.sharedContext.accountManager.accessChallengeData(),
|
context.sharedContext.accountManager.accessChallengeData(),
|
||||||
combineLatest(twoStepAuth.get(), twoStepAuthDataValue.get()),
|
combineLatest(twoStepAuth.get(), twoStepAuthDataValue.get()),
|
||||||
context.engine.data.subscribe(TelegramEngine.EngineData.Item.Configuration.App()),
|
context.engine.data.subscribe(TelegramEngine.EngineData.Item.Configuration.App()),
|
||||||
context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId))
|
context.engine.data.subscribe(TelegramEngine.EngineData.Item.Peer.Peer(id: context.account.peerId)),
|
||||||
|
loginEmail
|
||||||
)
|
)
|
||||||
|> map { presentationData, state, privacySettings, noticeView, sharedData, recentPeers, blockedPeersState, activeWebsitesState, accessChallengeData, twoStepAuth, appConfiguration, accountPeer -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
|> map { presentationData, state, privacySettings, noticeView, sharedData, recentPeers, blockedPeersState, activeWebsitesState, accessChallengeData, twoStepAuth, appConfiguration, accountPeer, loginEmail -> (ItemListControllerState, (ItemListNodeState, Any)) in
|
||||||
var canAutoarchive = false
|
var canAutoarchive = false
|
||||||
if let data = appConfiguration.data, let hasAutoarchive = data["autoarchive_setting_available"] as? Bool {
|
if let data = appConfiguration.data, let hasAutoarchive = data["autoarchive_setting_available"] as? Bool {
|
||||||
canAutoarchive = hasAutoarchive
|
canAutoarchive = hasAutoarchive
|
||||||
@ -971,7 +1039,7 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
|
|||||||
let isPremium = accountPeer?.isPremium ?? false
|
let isPremium = accountPeer?.isPremium ?? false
|
||||||
let isPremiumDisabled = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }).isPremiumDisabled
|
let isPremiumDisabled = PremiumConfiguration.with(appConfiguration: context.currentAppConfiguration.with { $0 }).isPremiumDisabled
|
||||||
|
|
||||||
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: privacyAndSecurityControllerEntries(presentationData: presentationData, state: state, privacySettings: privacySettings, accessChallengeData: accessChallengeData.data, blockedPeerCount: blockedPeersState.totalCount, activeWebsitesCount: activeWebsitesState.sessions.count, hasTwoStepAuth: twoStepAuth.0, twoStepAuthData: twoStepAuth.1, canAutoarchive: canAutoarchive, isPremiumDisabled: isPremiumDisabled, isPremium: isPremium), style: .blocks, ensureVisibleItemTag: focusOnItemTag, animateChanges: false)
|
let listState = ItemListNodeState(presentationData: ItemListPresentationData(presentationData), entries: privacyAndSecurityControllerEntries(presentationData: presentationData, state: state, privacySettings: privacySettings, accessChallengeData: accessChallengeData.data, blockedPeerCount: blockedPeersState.totalCount, activeWebsitesCount: activeWebsitesState.sessions.count, hasTwoStepAuth: twoStepAuth.0, twoStepAuthData: twoStepAuth.1, canAutoarchive: canAutoarchive, isPremiumDisabled: isPremiumDisabled, isPremium: isPremium, loginEmail: loginEmail), style: .blocks, ensureVisibleItemTag: focusOnItemTag, animateChanges: false)
|
||||||
|
|
||||||
return (controllerState, (listState, arguments))
|
return (controllerState, (listState, arguments))
|
||||||
}
|
}
|
||||||
@ -997,5 +1065,60 @@ public func privacyAndSecurityController(context: AccountContext, initialSetting
|
|||||||
updateHasTwoStepAuth()
|
updateHasTwoStepAuth()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
setupEmailImpl = { emailPattern in
|
||||||
|
let presentationData = context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
var dismissEmailControllerImpl: (() -> Void)?
|
||||||
|
let emailController = AuthorizationSequenceEmailEntryController(presentationData: presentationData, mode: emailPattern != nil ? .change : .setup, back: {
|
||||||
|
dismissEmailControllerImpl?()
|
||||||
|
})
|
||||||
|
emailController.proceedWithEmail = { [weak emailController] email in
|
||||||
|
emailController?.inProgress = true
|
||||||
|
|
||||||
|
actionsDisposable.add((sendLoginEmailChangeCode(account: context.account, email: email)
|
||||||
|
|> deliverOnMainQueue).start(next: { data in
|
||||||
|
var dismissCodeControllerImpl: (() -> Void)?
|
||||||
|
let codeController = AuthorizationSequenceCodeEntryController(presentationData: presentationData, openUrl: { _ in }, back: {
|
||||||
|
dismissCodeControllerImpl?()
|
||||||
|
})
|
||||||
|
codeController.loginWithCode = { code in
|
||||||
|
|
||||||
|
}
|
||||||
|
codeController.signInWithApple = {
|
||||||
|
|
||||||
|
}
|
||||||
|
codeController.updateData(number: "", email: email, codeType: .email(emailPattern: "", length: data.length, nextPhoneLoginDate: nil, appleSignInAllowed: false, setup: true), nextType: nil, timeout: nil, termsOfService: nil)
|
||||||
|
pushControllerImpl?(codeController, true)
|
||||||
|
dismissCodeControllerImpl = { [weak codeController] in
|
||||||
|
codeController?.dismiss()
|
||||||
|
}
|
||||||
|
}, error: { [weak emailController] error in
|
||||||
|
emailController?.inProgress = false
|
||||||
|
|
||||||
|
let text: String
|
||||||
|
switch error {
|
||||||
|
case .limitExceeded:
|
||||||
|
text = presentationData.strings.Login_CodeFloodError
|
||||||
|
case .generic, .codeExpired:
|
||||||
|
text = presentationData.strings.Login_UnknownError
|
||||||
|
case .timeout:
|
||||||
|
text = presentationData.strings.Login_NetworkError
|
||||||
|
}
|
||||||
|
|
||||||
|
presentControllerImpl?(standardTextAlertController(theme: AlertControllerTheme(presentationData: presentationData), title: nil, text: text, actions: [TextAlertAction(type: .defaultAction, title: presentationData.strings.Common_OK, action: {})]))
|
||||||
|
}, completed: { [weak emailController] in
|
||||||
|
emailController?.inProgress = false
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
emailController.signInWithApple = {
|
||||||
|
|
||||||
|
}
|
||||||
|
emailController.updateData(appleSignInAllowed: true)
|
||||||
|
pushControllerImpl?(emailController, true)
|
||||||
|
|
||||||
|
dismissEmailControllerImpl = { [weak emailController] in
|
||||||
|
emailController?.dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return controller
|
return controller
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,7 @@ import TelegramCore
|
|||||||
import SwiftSignalKit
|
import SwiftSignalKit
|
||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
import AccountContext
|
import AccountContext
|
||||||
import AuthorizationUI
|
import AuthorizationUtils
|
||||||
import AnimatedStickerNode
|
import AnimatedStickerNode
|
||||||
import TelegramAnimatedStickerNode
|
import TelegramAnimatedStickerNode
|
||||||
|
|
||||||
|
@ -23,11 +23,15 @@ public final class SolidRoundedButtonTheme: Equatable {
|
|||||||
public let backgroundColor: UIColor
|
public let backgroundColor: UIColor
|
||||||
public let backgroundColors: [UIColor]
|
public let backgroundColors: [UIColor]
|
||||||
public let foregroundColor: UIColor
|
public let foregroundColor: UIColor
|
||||||
|
public let disabledBackgroundColor: UIColor?
|
||||||
|
public let disabledForegroundColor: UIColor?
|
||||||
|
|
||||||
public init(backgroundColor: UIColor, backgroundColors: [UIColor] = [], foregroundColor: UIColor) {
|
public init(backgroundColor: UIColor, backgroundColors: [UIColor] = [], foregroundColor: UIColor, disabledBackgroundColor: UIColor? = nil, disabledForegroundColor: UIColor? = nil) {
|
||||||
self.backgroundColor = backgroundColor
|
self.backgroundColor = backgroundColor
|
||||||
self.backgroundColors = backgroundColors
|
self.backgroundColors = backgroundColors
|
||||||
self.foregroundColor = foregroundColor
|
self.foregroundColor = foregroundColor
|
||||||
|
self.disabledBackgroundColor = disabledBackgroundColor
|
||||||
|
self.disabledForegroundColor = disabledForegroundColor
|
||||||
}
|
}
|
||||||
|
|
||||||
public static func ==(lhs: SolidRoundedButtonTheme, rhs: SolidRoundedButtonTheme) -> Bool {
|
public static func ==(lhs: SolidRoundedButtonTheme, rhs: SolidRoundedButtonTheme) -> Bool {
|
||||||
@ -40,6 +44,12 @@ public final class SolidRoundedButtonTheme: Equatable {
|
|||||||
if lhs.foregroundColor != rhs.foregroundColor {
|
if lhs.foregroundColor != rhs.foregroundColor {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
if lhs.disabledBackgroundColor != rhs.disabledBackgroundColor {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
if lhs.disabledForegroundColor != rhs.disabledForegroundColor {
|
||||||
|
return false
|
||||||
|
}
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -86,6 +96,14 @@ public final class SolidRoundedButtonNode: ASDisplayNode {
|
|||||||
public var pressed: (() -> Void)?
|
public var pressed: (() -> Void)?
|
||||||
public var validLayout: CGFloat?
|
public var validLayout: CGFloat?
|
||||||
|
|
||||||
|
public var isEnabled: Bool = true {
|
||||||
|
didSet {
|
||||||
|
if self.isEnabled != oldValue {
|
||||||
|
self.updateColors(animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public var title: String? {
|
public var title: String? {
|
||||||
didSet {
|
didSet {
|
||||||
if let width = self.validLayout {
|
if let width = self.validLayout {
|
||||||
@ -220,7 +238,7 @@ public final class SolidRoundedButtonNode: ASDisplayNode {
|
|||||||
|
|
||||||
self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
|
self.buttonNode.addTarget(self, action: #selector(self.buttonPressed), forControlEvents: .touchUpInside)
|
||||||
self.buttonNode.highligthedChanged = { [weak self] highlighted in
|
self.buttonNode.highligthedChanged = { [weak self] highlighted in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self, strongSelf.isEnabled {
|
||||||
if highlighted {
|
if highlighted {
|
||||||
strongSelf.buttonBackgroundNode.layer.removeAnimation(forKey: "opacity")
|
strongSelf.buttonBackgroundNode.layer.removeAnimation(forKey: "opacity")
|
||||||
strongSelf.buttonBackgroundNode.alpha = 0.55
|
strongSelf.buttonBackgroundNode.alpha = 0.55
|
||||||
@ -312,36 +330,20 @@ public final class SolidRoundedButtonNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func animateTitle(from title: String) {
|
|
||||||
let originalTitle = self.title ?? ""
|
|
||||||
self.title = title
|
|
||||||
|
|
||||||
Queue.mainQueue().justDispatch {
|
|
||||||
if let snapshotView = self.titleNode.view.snapshotView(afterScreenUpdates: false) {
|
|
||||||
snapshotView.frame = self.titleNode.frame
|
|
||||||
|
|
||||||
self.view.insertSubview(snapshotView, aboveSubview: self.titleNode.view)
|
|
||||||
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak snapshotView] _ in
|
|
||||||
snapshotView?.removeFromSuperview()
|
|
||||||
})
|
|
||||||
self.titleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
|
||||||
|
|
||||||
self.title = originalTitle
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
public func animateTitle(to title: String) {
|
public func animateTitle(to title: String) {
|
||||||
if let snapshotView = self.titleNode.view.snapshotView(afterScreenUpdates: false) {
|
Queue.mainQueue().justDispatch {
|
||||||
snapshotView.frame = self.titleNode.frame
|
if let snapshotView = self.view.snapshotView(afterScreenUpdates: false) {
|
||||||
|
snapshotView.frame = self.bounds
|
||||||
|
|
||||||
self.view.insertSubview(snapshotView, aboveSubview: self.titleNode.view)
|
self.view.addSubview(snapshotView)
|
||||||
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak snapshotView] _ in
|
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak snapshotView] _ in
|
||||||
snapshotView?.removeFromSuperview()
|
snapshotView?.removeFromSuperview()
|
||||||
})
|
})
|
||||||
self.titleNode.layer.animateAlpha(from: 0.0, to: 1.0, duration: 0.2)
|
}
|
||||||
|
|
||||||
self.title = title
|
self.title = title
|
||||||
|
self.buttonBackgroundNode.backgroundColor = self.theme.disabledBackgroundColor
|
||||||
|
self.isEnabled = false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -459,6 +461,33 @@ public final class SolidRoundedButtonNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.iconNode.image = generateTintedImage(image: self.iconNode.image, color: theme.foregroundColor)
|
||||||
|
self.animationNode?.customColor = theme.foregroundColor
|
||||||
|
|
||||||
|
self.updateColors(animated: false)
|
||||||
|
|
||||||
|
self.updateShimmerParameters()
|
||||||
|
}
|
||||||
|
|
||||||
|
func updateColors(animated: Bool) {
|
||||||
|
if animated {
|
||||||
|
if let snapshotView = self.view.snapshotView(afterScreenUpdates: false) {
|
||||||
|
self.view.addSubview(snapshotView)
|
||||||
|
snapshotView.layer.animateAlpha(from: 1.0, to: 0.0, duration: 0.2, removeOnCompletion: false, completion: { [weak snapshotView] _ in
|
||||||
|
snapshotView?.removeFromSuperview()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if !self.isEnabled, let disabledBackgroundColor = self.theme.disabledBackgroundColor, let disabledForegroundColor = self.theme.disabledForegroundColor {
|
||||||
|
self.titleNode.attributedText = NSAttributedString(string: self.title ?? "", font: self.font == .bold ? Font.semibold(self.fontSize) : Font.regular(self.fontSize), textColor: disabledForegroundColor)
|
||||||
|
self.subtitleNode.attributedText = NSAttributedString(string: self.subtitle ?? "", font: Font.regular(14.0), textColor: disabledForegroundColor)
|
||||||
|
|
||||||
|
self.buttonBackgroundNode.backgroundColor = disabledBackgroundColor
|
||||||
|
} else {
|
||||||
|
self.titleNode.attributedText = NSAttributedString(string: self.title ?? "", font: self.font == .bold ? Font.semibold(self.fontSize) : Font.regular(self.fontSize), textColor: self.theme.foregroundColor)
|
||||||
|
self.subtitleNode.attributedText = NSAttributedString(string: self.subtitle ?? "", font: Font.regular(14.0), textColor: self.theme.foregroundColor)
|
||||||
|
|
||||||
self.buttonBackgroundNode.backgroundColor = theme.backgroundColor
|
self.buttonBackgroundNode.backgroundColor = theme.backgroundColor
|
||||||
if theme.backgroundColors.count > 1 {
|
if theme.backgroundColors.count > 1 {
|
||||||
self.buttonBackgroundNode.backgroundColor = nil
|
self.buttonBackgroundNode.backgroundColor = nil
|
||||||
@ -481,18 +510,11 @@ public final class SolidRoundedButtonNode: ASDisplayNode {
|
|||||||
self.buttonBackgroundNode.image = nil
|
self.buttonBackgroundNode.image = nil
|
||||||
self.buttonBackgroundAnimationView?.image = nil
|
self.buttonBackgroundAnimationView?.image = nil
|
||||||
}
|
}
|
||||||
|
}
|
||||||
self.titleNode.attributedText = NSAttributedString(string: self.title ?? "", font: self.font == .bold ? Font.semibold(self.fontSize) : Font.regular(self.fontSize), textColor: theme.foregroundColor)
|
|
||||||
self.subtitleNode.attributedText = NSAttributedString(string: self.subtitle ?? "", font: Font.regular(14.0), textColor: theme.foregroundColor)
|
|
||||||
|
|
||||||
self.iconNode.image = generateTintedImage(image: self.iconNode.image, color: theme.foregroundColor)
|
|
||||||
self.animationNode?.customColor = theme.foregroundColor
|
|
||||||
|
|
||||||
if let width = self.validLayout {
|
if let width = self.validLayout {
|
||||||
_ = self.updateLayout(width: width, transition: .immediate)
|
_ = self.updateLayout(width: width, transition: .immediate)
|
||||||
}
|
}
|
||||||
|
|
||||||
self.updateShimmerParameters()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func sizeThatFits(_ constrainedSize: CGSize) -> CGSize {
|
public func sizeThatFits(_ constrainedSize: CGSize) -> CGSize {
|
||||||
@ -532,7 +554,7 @@ public final class SolidRoundedButtonNode: ASDisplayNode {
|
|||||||
transition.updateFrame(node: self.buttonNode, frame: buttonFrame)
|
transition.updateFrame(node: self.buttonNode, frame: buttonFrame)
|
||||||
|
|
||||||
if self.title != self.titleNode.attributedText?.string {
|
if self.title != self.titleNode.attributedText?.string {
|
||||||
self.titleNode.attributedText = NSAttributedString(string: self.title ?? "", font: self.font == .bold ? Font.semibold(self.fontSize) : Font.regular(self.fontSize), textColor: self.theme.foregroundColor)
|
self.titleNode.attributedText = NSAttributedString(string: self.title ?? "", font: self.font == .bold ? Font.semibold(self.fontSize) : Font.regular(self.fontSize), textColor: self.isEnabled ? self.theme.foregroundColor : (self.theme.disabledForegroundColor ?? self.theme.foregroundColor))
|
||||||
}
|
}
|
||||||
|
|
||||||
let iconSize: CGSize
|
let iconSize: CGSize
|
||||||
@ -595,8 +617,10 @@ public final class SolidRoundedButtonNode: ASDisplayNode {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@objc private func buttonPressed() {
|
@objc private func buttonPressed() {
|
||||||
|
if self.isEnabled {
|
||||||
self.pressed?()
|
self.pressed?()
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func transitionToProgress() {
|
public func transitionToProgress() {
|
||||||
guard self.progressNode == nil else {
|
guard self.progressNode == nil else {
|
||||||
|
@ -39,6 +39,7 @@ swift_library(
|
|||||||
"//submodules/TelegramUI/Components/AnimationCache:AnimationCache",
|
"//submodules/TelegramUI/Components/AnimationCache:AnimationCache",
|
||||||
"//submodules/TelegramUI/Components/MultiAnimationRenderer:MultiAnimationRenderer",
|
"//submodules/TelegramUI/Components/MultiAnimationRenderer:MultiAnimationRenderer",
|
||||||
"//submodules/StickerPeekUI:StickerPeekUI",
|
"//submodules/StickerPeekUI:StickerPeekUI",
|
||||||
|
"//submodules/Pasteboard:Pasteboard",
|
||||||
],
|
],
|
||||||
visibility = [
|
visibility = [
|
||||||
"//visibility:public",
|
"//visibility:public",
|
||||||
|
@ -14,6 +14,7 @@ import ShimmerEffect
|
|||||||
import EntityKeyboard
|
import EntityKeyboard
|
||||||
import AnimationCache
|
import AnimationCache
|
||||||
import MultiAnimationRenderer
|
import MultiAnimationRenderer
|
||||||
|
import TextFormat
|
||||||
|
|
||||||
private let nativeItemSize = 36.0
|
private let nativeItemSize = 36.0
|
||||||
private let minItemsPerRow = 8
|
private let minItemsPerRow = 8
|
||||||
@ -24,17 +25,14 @@ private let containerInsets = UIEdgeInsets(top: 0.0, left: 12.0, bottom: 0.0, ri
|
|||||||
class ItemLayout {
|
class ItemLayout {
|
||||||
let width: CGFloat
|
let width: CGFloat
|
||||||
let itemsCount: Int
|
let itemsCount: Int
|
||||||
let hasTitle: Bool
|
|
||||||
let itemsPerRow: Int
|
let itemsPerRow: Int
|
||||||
let visibleItemSize: CGFloat
|
let visibleItemSize: CGFloat
|
||||||
let horizontalSpacing: CGFloat
|
let horizontalSpacing: CGFloat
|
||||||
let itemTopOffset: CGFloat
|
|
||||||
let height: CGFloat
|
let height: CGFloat
|
||||||
|
|
||||||
init(width: CGFloat, itemsCount: Int, hasTitle: Bool) {
|
init(width: CGFloat, itemsCount: Int) {
|
||||||
self.width = width
|
self.width = width
|
||||||
self.itemsCount = itemsCount
|
self.itemsCount = itemsCount
|
||||||
self.hasTitle = hasTitle
|
|
||||||
|
|
||||||
let itemHorizontalSpace = width - containerInsets.left - containerInsets.right
|
let itemHorizontalSpace = width - containerInsets.left - containerInsets.right
|
||||||
self.itemsPerRow = max(minItemsPerRow, Int((itemHorizontalSpace + minSpacing) / (nativeItemSize + minSpacing)))
|
self.itemsPerRow = max(minItemsPerRow, Int((itemHorizontalSpace + minSpacing) / (nativeItemSize + minSpacing)))
|
||||||
@ -45,8 +43,7 @@ class ItemLayout {
|
|||||||
|
|
||||||
let numRowsInGroup = (itemsCount + (self.itemsPerRow - 1)) / self.itemsPerRow
|
let numRowsInGroup = (itemsCount + (self.itemsPerRow - 1)) / self.itemsPerRow
|
||||||
|
|
||||||
self.itemTopOffset = hasTitle ? 61.0 : 0.0
|
self.height = CGFloat(numRowsInGroup) * visibleItemSize + CGFloat(max(0, numRowsInGroup - 1)) * verticalSpacing
|
||||||
self.height = itemTopOffset + CGFloat(numRowsInGroup) * visibleItemSize + CGFloat(max(0, numRowsInGroup - 1)) * verticalSpacing
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func frame(itemIndex: Int) -> CGRect {
|
func frame(itemIndex: Int) -> CGRect {
|
||||||
@ -56,7 +53,7 @@ class ItemLayout {
|
|||||||
return CGRect(
|
return CGRect(
|
||||||
origin: CGPoint(
|
origin: CGPoint(
|
||||||
x: containerInsets.left + CGFloat(column) * (self.visibleItemSize + self.horizontalSpacing),
|
x: containerInsets.left + CGFloat(column) * (self.visibleItemSize + self.horizontalSpacing),
|
||||||
y: self.itemTopOffset + CGFloat(row) * (self.visibleItemSize + verticalSpacing)
|
y: CGFloat(row) * (self.visibleItemSize + verticalSpacing)
|
||||||
),
|
),
|
||||||
size: CGSize(
|
size: CGSize(
|
||||||
width: self.visibleItemSize,
|
width: self.visibleItemSize,
|
||||||
@ -96,8 +93,8 @@ final class StickerPackEmojisItem: GridItem {
|
|||||||
self.isEmpty = isEmpty
|
self.isEmpty = isEmpty
|
||||||
|
|
||||||
self.fillsRowWithDynamicHeight = { width in
|
self.fillsRowWithDynamicHeight = { width in
|
||||||
let layout = ItemLayout(width: width, itemsCount: items.count, hasTitle: title != nil)
|
let layout = ItemLayout(width: width, itemsCount: items.count)
|
||||||
return layout.height
|
return layout.height + (title != nil ? 61.0 : 0.0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,11 +125,13 @@ final class StickerPackEmojisItemNode: GridItemNode {
|
|||||||
private var visibleItemLayers: [EmojiPagerContentComponent.View.ItemLayer.Key: EmojiPagerContentComponent.View.ItemLayer] = [:]
|
private var visibleItemLayers: [EmojiPagerContentComponent.View.ItemLayer.Key: EmojiPagerContentComponent.View.ItemLayer] = [:]
|
||||||
private var visibleItemPlaceholderViews: [EmojiPagerContentComponent.View.ItemLayer.Key: EmojiPagerContentComponent.View.ItemPlaceholderView] = [:]
|
private var visibleItemPlaceholderViews: [EmojiPagerContentComponent.View.ItemLayer.Key: EmojiPagerContentComponent.View.ItemPlaceholderView] = [:]
|
||||||
|
|
||||||
|
private let containerNode: ASDisplayNode
|
||||||
private let titleNode: ImmediateTextNode
|
private let titleNode: ImmediateTextNode
|
||||||
private let subtitleNode: ImmediateTextNode
|
private let subtitleNode: ImmediateTextNode
|
||||||
private let buttonNode: HighlightableButtonNode
|
private let buttonNode: HighlightableButtonNode
|
||||||
|
|
||||||
override init() {
|
override init() {
|
||||||
|
self.containerNode = ASDisplayNode()
|
||||||
self.titleNode = ImmediateTextNode()
|
self.titleNode = ImmediateTextNode()
|
||||||
self.subtitleNode = ImmediateTextNode()
|
self.subtitleNode = ImmediateTextNode()
|
||||||
self.buttonNode = HighlightableButtonNode(pointerStyle: nil)
|
self.buttonNode = HighlightableButtonNode(pointerStyle: nil)
|
||||||
@ -141,6 +140,7 @@ final class StickerPackEmojisItemNode: GridItemNode {
|
|||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
|
self.addSubnode(self.containerNode)
|
||||||
self.addSubnode(self.titleNode)
|
self.addSubnode(self.titleNode)
|
||||||
self.addSubnode(self.subtitleNode)
|
self.addSubnode(self.subtitleNode)
|
||||||
self.addSubnode(self.buttonNode)
|
self.addSubnode(self.buttonNode)
|
||||||
@ -191,6 +191,98 @@ final class StickerPackEmojisItemNode: GridItemNode {
|
|||||||
self?.standaloneShimmerEffect?.updateLayer()
|
self?.standaloneShimmerEffect?.updateLayer()
|
||||||
}
|
}
|
||||||
self.boundsChangeTrackerLayer = boundsChangeTrackerLayer
|
self.boundsChangeTrackerLayer = boundsChangeTrackerLayer
|
||||||
|
|
||||||
|
let gestureRecognizer = TapLongTapOrDoubleTapGestureRecognizer(target: self, action: #selector(self.tapGesture(_:)))
|
||||||
|
gestureRecognizer.longTap = { [weak self] point, _ in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
if let (item, itemFrame) = strongSelf.item(atPoint: point), let file = item.itemFile {
|
||||||
|
var text = "."
|
||||||
|
var emojiAttribute: ChatTextInputTextCustomEmojiAttribute?
|
||||||
|
loop: for attribute in file.attributes {
|
||||||
|
switch attribute {
|
||||||
|
case let .CustomEmoji(_, displayText, packReference):
|
||||||
|
text = displayText
|
||||||
|
emojiAttribute = ChatTextInputTextCustomEmojiAttribute(stickerPack: packReference, fileId: file.fileId.id, file: file)
|
||||||
|
break loop
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let emojiAttribute = emojiAttribute {
|
||||||
|
strongSelf.item?.interaction.emojiLongPressed(text, emojiAttribute, strongSelf.containerNode, itemFrame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.containerNode.view.addGestureRecognizer(gestureRecognizer)
|
||||||
|
}
|
||||||
|
|
||||||
|
@objc private func tapGesture(_ recognizer: TapLongTapOrDoubleTapGestureRecognizer) {
|
||||||
|
switch recognizer.state {
|
||||||
|
case .ended:
|
||||||
|
if let (gesture, location) = recognizer.lastRecognizedGestureAndLocation, let (item, _) = self.item(atPoint: location), let file = item.itemFile {
|
||||||
|
if case .tap = gesture {
|
||||||
|
var text = "."
|
||||||
|
var emojiAttribute: ChatTextInputTextCustomEmojiAttribute?
|
||||||
|
loop: for attribute in file.attributes {
|
||||||
|
switch attribute {
|
||||||
|
case let .CustomEmoji(_, displayText, packReference):
|
||||||
|
text = displayText
|
||||||
|
emojiAttribute = ChatTextInputTextCustomEmojiAttribute(stickerPack: packReference, fileId: file.fileId.id, file: file)
|
||||||
|
break loop
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let emojiAttribute = emojiAttribute {
|
||||||
|
self.item?.interaction.emojiSelected(text, emojiAttribute)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func item(atPoint point: CGPoint, extendedHitRange: Bool = false) -> (EmojiPagerContentComponent.Item, CGRect)? {
|
||||||
|
let localPoint = point
|
||||||
|
|
||||||
|
var closestItem: (key: EmojiPagerContentComponent.View.ItemLayer.Key, distance: CGFloat)?
|
||||||
|
|
||||||
|
for (key, itemLayer) in self.visibleItemLayers {
|
||||||
|
if extendedHitRange {
|
||||||
|
let position = CGPoint(x: itemLayer.frame.midX, y: itemLayer.frame.midY)
|
||||||
|
let distance = CGPoint(x: localPoint.x - position.x, y: localPoint.y - position.y)
|
||||||
|
let distance2 = distance.x * distance.x + distance.y * distance.y
|
||||||
|
if distance2 > pow(max(itemLayer.bounds.width, itemLayer.bounds.height), 2.0) {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
if let closestItemValue = closestItem {
|
||||||
|
if closestItemValue.distance > distance2 {
|
||||||
|
closestItem = (key, distance2)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
closestItem = (key, distance2)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if itemLayer.frame.contains(localPoint) {
|
||||||
|
return (itemLayer.item, itemLayer.frame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if let key = closestItem?.key {
|
||||||
|
if let itemLayer = self.visibleItemLayers[key] {
|
||||||
|
return (itemLayer.item, itemLayer.frame)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
private var size = CGSize()
|
private var size = CGSize()
|
||||||
@ -233,13 +325,15 @@ final class StickerPackEmojisItemNode: GridItemNode {
|
|||||||
var validIds = Set<EmojiPagerContentComponent.View.ItemLayer.Key>()
|
var validIds = Set<EmojiPagerContentComponent.View.ItemLayer.Key>()
|
||||||
|
|
||||||
let itemLayout: ItemLayout
|
let itemLayout: ItemLayout
|
||||||
if let current = self.itemLayout, current.width == self.size.width && current.itemsCount == items.count && current.hasTitle == (item.title != nil) {
|
if let current = self.itemLayout, current.width == self.size.width && current.itemsCount == items.count {
|
||||||
itemLayout = current
|
itemLayout = current
|
||||||
} else {
|
} else {
|
||||||
itemLayout = ItemLayout(width: self.size.width, itemsCount: items.count, hasTitle: item.title != nil)
|
itemLayout = ItemLayout(width: self.size.width, itemsCount: items.count)
|
||||||
self.itemLayout = itemLayout
|
self.itemLayout = itemLayout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
self.containerNode.frame = CGRect(origin: CGPoint(x: 0.0, y: item.title != nil ? 61.0 : 0.0), size: CGSize(width: itemLayout.width, height: itemLayout.height))
|
||||||
|
|
||||||
for index in 0 ..< items.count {
|
for index in 0 ..< items.count {
|
||||||
let item = items[index]
|
let item = items[index]
|
||||||
let itemId = EmojiPagerContentComponent.View.ItemLayer.Key(
|
let itemId = EmojiPagerContentComponent.View.ItemLayer.Key(
|
||||||
@ -325,7 +419,7 @@ final class StickerPackEmojisItemNode: GridItemNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
self.layer.addSublayer(itemLayer)
|
self.containerNode.layer.addSublayer(itemLayer)
|
||||||
self.visibleItemLayers[itemId] = itemLayer
|
self.visibleItemLayers[itemId] = itemLayer
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -143,7 +143,7 @@ final class StickerPackPreviewControllerNode: ViewControllerTracingNode, UIScrol
|
|||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
|
|
||||||
self.interaction = StickerPackPreviewInteraction(playAnimatedStickers: false, addStickerPack: { _, _ in }, removeStickerPack: { _ in })
|
self.interaction = StickerPackPreviewInteraction(playAnimatedStickers: false, addStickerPack: { _, _ in }, removeStickerPack: { _ in }, emojiSelected: { _, _ in }, emojiLongPressed: { _, _, _, _ in })
|
||||||
|
|
||||||
self.backgroundColor = nil
|
self.backgroundColor = nil
|
||||||
self.isOpaque = false
|
self.isOpaque = false
|
||||||
|
@ -12,6 +12,7 @@ import TelegramAnimatedStickerNode
|
|||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
import ShimmerEffect
|
import ShimmerEffect
|
||||||
import StickerPeekUI
|
import StickerPeekUI
|
||||||
|
import TextFormat
|
||||||
|
|
||||||
final class StickerPackPreviewInteraction {
|
final class StickerPackPreviewInteraction {
|
||||||
var previewedItem: StickerPreviewPeekItem?
|
var previewedItem: StickerPreviewPeekItem?
|
||||||
@ -19,11 +20,15 @@ final class StickerPackPreviewInteraction {
|
|||||||
|
|
||||||
let addStickerPack: (StickerPackCollectionInfo, [StickerPackItem]) -> Void
|
let addStickerPack: (StickerPackCollectionInfo, [StickerPackItem]) -> Void
|
||||||
let removeStickerPack: (StickerPackCollectionInfo) -> Void
|
let removeStickerPack: (StickerPackCollectionInfo) -> Void
|
||||||
|
let emojiSelected: (String, ChatTextInputTextCustomEmojiAttribute) -> Void
|
||||||
|
let emojiLongPressed: (String, ChatTextInputTextCustomEmojiAttribute, ASDisplayNode, CGRect) -> Void
|
||||||
|
|
||||||
init(playAnimatedStickers: Bool, addStickerPack: @escaping (StickerPackCollectionInfo, [StickerPackItem]) -> Void, removeStickerPack: @escaping (StickerPackCollectionInfo) -> Void) {
|
init(playAnimatedStickers: Bool, addStickerPack: @escaping (StickerPackCollectionInfo, [StickerPackItem]) -> Void, removeStickerPack: @escaping (StickerPackCollectionInfo) -> Void, emojiSelected: @escaping (String, ChatTextInputTextCustomEmojiAttribute) -> Void, emojiLongPressed: @escaping (String, ChatTextInputTextCustomEmojiAttribute, ASDisplayNode, CGRect) -> Void) {
|
||||||
self.playAnimatedStickers = playAnimatedStickers
|
self.playAnimatedStickers = playAnimatedStickers
|
||||||
self.addStickerPack = addStickerPack
|
self.addStickerPack = addStickerPack
|
||||||
self.removeStickerPack = removeStickerPack
|
self.removeStickerPack = removeStickerPack
|
||||||
|
self.emojiSelected = emojiSelected
|
||||||
|
self.emojiLongPressed = emojiLongPressed
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -21,6 +21,7 @@ import PresentationDataUtils
|
|||||||
import StickerPeekUI
|
import StickerPeekUI
|
||||||
import AnimationCache
|
import AnimationCache
|
||||||
import MultiAnimationRenderer
|
import MultiAnimationRenderer
|
||||||
|
import Pasteboard
|
||||||
|
|
||||||
private enum StickerPackPreviewGridEntry: Comparable, Identifiable {
|
private enum StickerPackPreviewGridEntry: Comparable, Identifiable {
|
||||||
case sticker(index: Int, stableId: Int, stickerItem: StickerPackItem?, isEmpty: Bool, isPremium: Bool, isLocked: Bool)
|
case sticker(index: Int, stableId: Int, stickerItem: StickerPackItem?, isEmpty: Bool, isPremium: Bool, isLocked: Bool)
|
||||||
@ -145,7 +146,21 @@ private final class StickerPackContainer: ASDisplayNode {
|
|||||||
var onReady: () -> Void = {}
|
var onReady: () -> Void = {}
|
||||||
var onError: () -> Void = {}
|
var onError: () -> Void = {}
|
||||||
|
|
||||||
init(index: Int, context: AccountContext, presentationData: PresentationData, stickerPacks: [StickerPackReference], decideNextAction: @escaping (StickerPackContainer, StickerPackAction) -> StickerPackNextAction, requestDismiss: @escaping () -> Void, expandProgressUpdated: @escaping (StickerPackContainer, ContainedViewLayoutTransition, ContainedViewLayoutTransition) -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?, openMention: @escaping (String) -> Void, controller: StickerPackScreenImpl?) {
|
init(
|
||||||
|
index: Int,
|
||||||
|
context: AccountContext,
|
||||||
|
presentationData: PresentationData,
|
||||||
|
stickerPacks: [StickerPackReference],
|
||||||
|
decideNextAction: @escaping (StickerPackContainer, StickerPackAction) -> StickerPackNextAction,
|
||||||
|
requestDismiss: @escaping () -> Void,
|
||||||
|
expandProgressUpdated: @escaping (StickerPackContainer, ContainedViewLayoutTransition, ContainedViewLayoutTransition) -> Void,
|
||||||
|
presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void,
|
||||||
|
sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?,
|
||||||
|
sendEmoji: ((String, ChatTextInputTextCustomEmojiAttribute) -> Void)?,
|
||||||
|
longPressEmoji: ((String, ChatTextInputTextCustomEmojiAttribute, ASDisplayNode, CGRect) -> Void)?,
|
||||||
|
openMention: @escaping (String) -> Void,
|
||||||
|
controller: StickerPackScreenImpl?)
|
||||||
|
{
|
||||||
self.index = index
|
self.index = index
|
||||||
self.context = context
|
self.context = context
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
@ -201,10 +216,16 @@ private final class StickerPackContainer: ASDisplayNode {
|
|||||||
|
|
||||||
var addStickerPackImpl: ((StickerPackCollectionInfo, [StickerPackItem]) -> Void)?
|
var addStickerPackImpl: ((StickerPackCollectionInfo, [StickerPackItem]) -> Void)?
|
||||||
var removeStickerPackImpl: ((StickerPackCollectionInfo) -> Void)?
|
var removeStickerPackImpl: ((StickerPackCollectionInfo) -> Void)?
|
||||||
|
var emojiSelectedImpl: ((String, ChatTextInputTextCustomEmojiAttribute) -> Void)?
|
||||||
|
var emojiLongPressedImpl: ((String, ChatTextInputTextCustomEmojiAttribute, ASDisplayNode, CGRect) -> Void)?
|
||||||
self.interaction = StickerPackPreviewInteraction(playAnimatedStickers: true, addStickerPack: { info, items in
|
self.interaction = StickerPackPreviewInteraction(playAnimatedStickers: true, addStickerPack: { info, items in
|
||||||
addStickerPackImpl?(info, items)
|
addStickerPackImpl?(info, items)
|
||||||
}, removeStickerPack: { info in
|
}, removeStickerPack: { info in
|
||||||
removeStickerPackImpl?(info)
|
removeStickerPackImpl?(info)
|
||||||
|
}, emojiSelected: { text, attribute in
|
||||||
|
emojiSelectedImpl?(text, attribute)
|
||||||
|
}, emojiLongPressed: { text, attribute, node, frame in
|
||||||
|
emojiLongPressedImpl?(text, attribute, node, frame)
|
||||||
})
|
})
|
||||||
|
|
||||||
super.init()
|
super.init()
|
||||||
@ -215,7 +236,6 @@ private final class StickerPackContainer: ASDisplayNode {
|
|||||||
self.addSubnode(self.actionAreaSeparatorNode)
|
self.addSubnode(self.actionAreaSeparatorNode)
|
||||||
self.addSubnode(self.buttonNode)
|
self.addSubnode(self.buttonNode)
|
||||||
|
|
||||||
// self.addSubnode(self.titleBackgroundnode)
|
|
||||||
self.titleContainer.addSubnode(self.titleNode)
|
self.titleContainer.addSubnode(self.titleNode)
|
||||||
self.addSubnode(self.titleContainer)
|
self.addSubnode(self.titleContainer)
|
||||||
self.addSubnode(self.titleSeparatorNode)
|
self.addSubnode(self.titleSeparatorNode)
|
||||||
@ -395,6 +415,14 @@ private final class StickerPackContainer: ASDisplayNode {
|
|||||||
let _ = strongSelf.context.engine.stickers.removeStickerPackInteractively(id: info.id, option: .delete).start()
|
let _ = strongSelf.context.engine.stickers.removeStickerPackInteractively(id: info.id, option: .delete).start()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
emojiSelectedImpl = { text, attribute in
|
||||||
|
sendEmoji?(text, attribute)
|
||||||
|
}
|
||||||
|
|
||||||
|
emojiLongPressedImpl = { text, attribute, node, frame in
|
||||||
|
longPressEmoji?(text, attribute, node, frame)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
deinit {
|
deinit {
|
||||||
@ -1017,13 +1045,13 @@ private final class StickerPackContainer: ASDisplayNode {
|
|||||||
if !self.currentStickerPacks.isEmpty {
|
if !self.currentStickerPacks.isEmpty {
|
||||||
var packsHeight = 0.0
|
var packsHeight = 0.0
|
||||||
for stickerPack in currentStickerPacks {
|
for stickerPack in currentStickerPacks {
|
||||||
let layout = ItemLayout(width: fillingWidth, itemsCount: stickerPack.1.count, hasTitle: true)
|
let layout = ItemLayout(width: fillingWidth, itemsCount: stickerPack.1.count)
|
||||||
packsHeight += layout.height
|
packsHeight += layout.height + 61.0
|
||||||
}
|
}
|
||||||
contentHeight = packsHeight + 8.0
|
contentHeight = packsHeight + 8.0
|
||||||
} else if let (info, items, _) = self.currentStickerPack {
|
} else if let (info, items, _) = self.currentStickerPack {
|
||||||
if info.id.namespace == Namespaces.ItemCollection.CloudEmojiPacks {
|
if info.id.namespace == Namespaces.ItemCollection.CloudEmojiPacks {
|
||||||
let layout = ItemLayout(width: fillingWidth, itemsCount: items.count, hasTitle: false)
|
let layout = ItemLayout(width: fillingWidth, itemsCount: items.count)
|
||||||
contentHeight = layout.height
|
contentHeight = layout.height
|
||||||
} else {
|
} else {
|
||||||
let rowCount = items.count / itemsPerRow + ((items.count % itemsPerRow) == 0 ? 0 : 1)
|
let rowCount = items.count / itemsPerRow + ((items.count % itemsPerRow) == 0 ? 0 : 1)
|
||||||
@ -1186,6 +1214,8 @@ private final class StickerPackScreenNode: ViewControllerTracingNode {
|
|||||||
private let dismissed: () -> Void
|
private let dismissed: () -> Void
|
||||||
private let presentInGlobalOverlay: (ViewController, Any?) -> Void
|
private let presentInGlobalOverlay: (ViewController, Any?) -> Void
|
||||||
private let sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?
|
private let sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?
|
||||||
|
private let sendEmoji: ((String, ChatTextInputTextCustomEmojiAttribute) -> Void)?
|
||||||
|
private let longPressEmoji: ((String, ChatTextInputTextCustomEmojiAttribute, ASDisplayNode, CGRect) -> Void)?
|
||||||
private let openMention: (String) -> Void
|
private let openMention: (String) -> Void
|
||||||
|
|
||||||
private let dimNode: ASDisplayNode
|
private let dimNode: ASDisplayNode
|
||||||
@ -1207,7 +1237,19 @@ private final class StickerPackScreenNode: ViewControllerTracingNode {
|
|||||||
var onReady: () -> Void = {}
|
var onReady: () -> Void = {}
|
||||||
var onError: () -> Void = {}
|
var onError: () -> Void = {}
|
||||||
|
|
||||||
init(context: AccountContext, controller: StickerPackScreenImpl, stickerPacks: [StickerPackReference], initialSelectedStickerPackIndex: Int, modalProgressUpdated: @escaping (CGFloat, ContainedViewLayoutTransition) -> Void, dismissed: @escaping () -> Void, presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?, openMention: @escaping (String) -> Void) {
|
init(
|
||||||
|
context: AccountContext,
|
||||||
|
controller: StickerPackScreenImpl,
|
||||||
|
stickerPacks: [StickerPackReference],
|
||||||
|
initialSelectedStickerPackIndex: Int,
|
||||||
|
modalProgressUpdated: @escaping (CGFloat, ContainedViewLayoutTransition) -> Void,
|
||||||
|
dismissed: @escaping () -> Void,
|
||||||
|
presentInGlobalOverlay: @escaping (ViewController, Any?) -> Void,
|
||||||
|
sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?,
|
||||||
|
sendEmoji: ((String, ChatTextInputTextCustomEmojiAttribute) -> Void)?,
|
||||||
|
longPressEmoji: ((String, ChatTextInputTextCustomEmojiAttribute, ASDisplayNode, CGRect) -> Void)?,
|
||||||
|
openMention: @escaping (String) -> Void)
|
||||||
|
{
|
||||||
self.context = context
|
self.context = context
|
||||||
self.controller = controller
|
self.controller = controller
|
||||||
self.presentationData = controller.presentationData
|
self.presentationData = controller.presentationData
|
||||||
@ -1217,6 +1259,8 @@ private final class StickerPackScreenNode: ViewControllerTracingNode {
|
|||||||
self.dismissed = dismissed
|
self.dismissed = dismissed
|
||||||
self.presentInGlobalOverlay = presentInGlobalOverlay
|
self.presentInGlobalOverlay = presentInGlobalOverlay
|
||||||
self.sendSticker = sendSticker
|
self.sendSticker = sendSticker
|
||||||
|
self.sendEmoji = sendEmoji
|
||||||
|
self.longPressEmoji = longPressEmoji
|
||||||
self.openMention = openMention
|
self.openMention = openMention
|
||||||
|
|
||||||
self.dimNode = ASDisplayNode()
|
self.dimNode = ASDisplayNode()
|
||||||
@ -1327,7 +1371,7 @@ private final class StickerPackScreenNode: ViewControllerTracingNode {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, presentInGlobalOverlay: presentInGlobalOverlay, sendSticker: sendSticker, openMention: openMention, controller: self.controller)
|
}, presentInGlobalOverlay: presentInGlobalOverlay, sendSticker: self.sendSticker, sendEmoji: self.sendEmoji, longPressEmoji: self.longPressEmoji, openMention: self.openMention, controller: self.controller)
|
||||||
container.onReady = { [weak self] in
|
container.onReady = { [weak self] in
|
||||||
self?.onReady()
|
self?.onReady()
|
||||||
}
|
}
|
||||||
@ -1518,6 +1562,7 @@ public final class StickerPackScreenImpl: ViewController {
|
|||||||
private let initialSelectedStickerPackIndex: Int
|
private let initialSelectedStickerPackIndex: Int
|
||||||
fileprivate weak var parentNavigationController: NavigationController?
|
fileprivate weak var parentNavigationController: NavigationController?
|
||||||
private let sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?
|
private let sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?
|
||||||
|
private let sendEmoji: ((String, ChatTextInputTextCustomEmojiAttribute) -> Void)?
|
||||||
|
|
||||||
private var controllerNode: StickerPackScreenNode {
|
private var controllerNode: StickerPackScreenNode {
|
||||||
return self.displayNode as! StickerPackScreenNode
|
return self.displayNode as! StickerPackScreenNode
|
||||||
@ -1539,13 +1584,23 @@ public final class StickerPackScreenImpl: ViewController {
|
|||||||
let animationCache: AnimationCache
|
let animationCache: AnimationCache
|
||||||
let animationRenderer: MultiAnimationRenderer
|
let animationRenderer: MultiAnimationRenderer
|
||||||
|
|
||||||
public init(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, stickerPacks: [StickerPackReference], selectedStickerPackIndex: Int = 0, parentNavigationController: NavigationController? = nil, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)? = nil, actionPerformed: (([(StickerPackCollectionInfo, [StickerPackItem], StickerPackScreenPerformedAction)]) -> Void)? = nil) {
|
public init(
|
||||||
|
context: AccountContext,
|
||||||
|
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil,
|
||||||
|
stickerPacks: [StickerPackReference],
|
||||||
|
selectedStickerPackIndex: Int = 0,
|
||||||
|
parentNavigationController: NavigationController? = nil,
|
||||||
|
sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)? = nil,
|
||||||
|
sendEmoji: ((String, ChatTextInputTextCustomEmojiAttribute) -> Void)?,
|
||||||
|
actionPerformed: (([(StickerPackCollectionInfo, [StickerPackItem], StickerPackScreenPerformedAction)]) -> Void)? = nil
|
||||||
|
) {
|
||||||
self.context = context
|
self.context = context
|
||||||
self.presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
|
self.presentationData = updatedPresentationData?.initial ?? context.sharedContext.currentPresentationData.with { $0 }
|
||||||
self.stickerPacks = stickerPacks
|
self.stickerPacks = stickerPacks
|
||||||
self.initialSelectedStickerPackIndex = selectedStickerPackIndex
|
self.initialSelectedStickerPackIndex = selectedStickerPackIndex
|
||||||
self.parentNavigationController = parentNavigationController
|
self.parentNavigationController = parentNavigationController
|
||||||
self.sendSticker = sendSticker
|
self.sendSticker = sendSticker
|
||||||
|
self.sendEmoji = sendEmoji
|
||||||
self.actionPerformed = actionPerformed
|
self.actionPerformed = actionPerformed
|
||||||
|
|
||||||
self.animationCache = AnimationCacheImpl(basePath: context.account.postbox.mediaBox.basePath + "/animation-cache", allocateTempFile: {
|
self.animationCache = AnimationCacheImpl(basePath: context.account.postbox.mediaBox.basePath + "/animation-cache", allocateTempFile: {
|
||||||
@ -1605,6 +1660,46 @@ public final class StickerPackScreenImpl: ViewController {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}, sendEmoji: self.sendEmoji.flatMap { [weak self] sendEmoji in
|
||||||
|
return { text, attribute in
|
||||||
|
sendEmoji(text, attribute)
|
||||||
|
self?.controllerNode.dismiss()
|
||||||
|
}
|
||||||
|
}, longPressEmoji: { [weak self] text, attribute, node, frame in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
var actions: [ContextMenuAction] = []
|
||||||
|
|
||||||
|
actions.append(ContextMenuAction(content: .text(title: strongSelf.presentationData.strings.Conversation_ContextMenuCopy, accessibilityLabel: strongSelf.presentationData.strings.Conversation_ContextMenuCopy), action: { [weak self] in
|
||||||
|
storeMessageTextInPasteboard(
|
||||||
|
text,
|
||||||
|
entities: [
|
||||||
|
MessageTextEntity(
|
||||||
|
range: 0 ..< (text as NSString).length,
|
||||||
|
type: .CustomEmoji(
|
||||||
|
stickerPack: attribute.stickerPack,
|
||||||
|
fileId: attribute.fileId
|
||||||
|
)
|
||||||
|
)
|
||||||
|
]
|
||||||
|
)
|
||||||
|
|
||||||
|
if let strongSelf = self, let file = attribute.file {
|
||||||
|
let presentationData = strongSelf.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
strongSelf.present(UndoOverlayController(presentationData: presentationData, content: .sticker(context: strongSelf.context, file: file, title: nil, text: presentationData.strings.Conversation_EmojiCopied, undoText: nil, customAction: nil), elevatedLayout: false, animateInAsReplacement: false, action: { _ in return false }), in: .current)
|
||||||
|
}
|
||||||
|
}))
|
||||||
|
|
||||||
|
let contextMenuController = ContextMenuController(actions: actions)
|
||||||
|
strongSelf.present(contextMenuController, in: .window(.root), with: ContextMenuControllerPresentationArguments(sourceNodeAndRect: { [weak self] in
|
||||||
|
if let strongSelf = self {
|
||||||
|
return (node, frame.insetBy(dx: -40.0, dy: 0.0), strongSelf.controllerNode, strongSelf.controllerNode.view.bounds)
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}))
|
||||||
}, openMention: { [weak self] mention in
|
}, openMention: { [weak self] mention in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
@ -1718,16 +1813,29 @@ public enum StickerPackScreenPerformedAction {
|
|||||||
case remove(positionInList: Int)
|
case remove(positionInList: Int)
|
||||||
}
|
}
|
||||||
|
|
||||||
public func StickerPackScreen(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil, mode: StickerPackPreviewControllerMode = .default, mainStickerPack: StickerPackReference, stickerPacks: [StickerPackReference], parentNavigationController: NavigationController? = nil, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)? = nil, actionPerformed: (([(StickerPackCollectionInfo, [StickerPackItem], StickerPackScreenPerformedAction)]) -> Void)? = nil, dismissed: (() -> Void)? = nil) -> ViewController {
|
public func StickerPackScreen(
|
||||||
//let stickerPacks = [mainStickerPack]
|
context: AccountContext,
|
||||||
let controller = StickerPackScreenImpl(context: context, stickerPacks: stickerPacks, selectedStickerPackIndex: stickerPacks.firstIndex(of: mainStickerPack) ?? 0, parentNavigationController: parentNavigationController, sendSticker: sendSticker, actionPerformed: actionPerformed)
|
updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)? = nil,
|
||||||
|
mode: StickerPackPreviewControllerMode = .default,
|
||||||
|
mainStickerPack: StickerPackReference,
|
||||||
|
stickerPacks: [StickerPackReference],
|
||||||
|
parentNavigationController: NavigationController? = nil,
|
||||||
|
sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)? = nil,
|
||||||
|
sendEmoji: ((String, ChatTextInputTextCustomEmojiAttribute) -> Void)? = nil,
|
||||||
|
actionPerformed: (([(StickerPackCollectionInfo, [StickerPackItem], StickerPackScreenPerformedAction)]) -> Void)? = nil,
|
||||||
|
dismissed: (() -> Void)? = nil) -> ViewController
|
||||||
|
{
|
||||||
|
let controller = StickerPackScreenImpl(
|
||||||
|
context: context,
|
||||||
|
stickerPacks: stickerPacks,
|
||||||
|
selectedStickerPackIndex: stickerPacks.firstIndex(of: mainStickerPack) ?? 0,
|
||||||
|
parentNavigationController: parentNavigationController,
|
||||||
|
sendSticker: sendSticker,
|
||||||
|
sendEmoji: sendEmoji,
|
||||||
|
actionPerformed: actionPerformed
|
||||||
|
)
|
||||||
controller.dismissed = dismissed
|
controller.dismissed = dismissed
|
||||||
return controller
|
return controller
|
||||||
|
|
||||||
// let controller = StickerPackPreviewController(context: context, updatedPresentationData: updatedPresentationData, stickerPack: mainStickerPack, mode: mode, parentNavigationController: parentNavigationController, actionPerformed: actionPerformed)
|
|
||||||
// controller.dismissed = dismissed
|
|
||||||
// controller.sendSticker = sendSticker
|
|
||||||
// return controller
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -595,6 +595,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||||||
dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) }
|
dict[1558266229] = { return Api.PopularContact.parse_popularContact($0) }
|
||||||
dict[512535275] = { return Api.PostAddress.parse_postAddress($0) }
|
dict[512535275] = { return Api.PostAddress.parse_postAddress($0) }
|
||||||
dict[1958953753] = { return Api.PremiumGiftOption.parse_premiumGiftOption($0) }
|
dict[1958953753] = { return Api.PremiumGiftOption.parse_premiumGiftOption($0) }
|
||||||
|
dict[-1225711938] = { return Api.PremiumSubscriptionOption.parse_premiumSubscriptionOption($0) }
|
||||||
dict[1124062251] = { return Api.PrivacyKey.parse_privacyKeyAddedByPhone($0) }
|
dict[1124062251] = { return Api.PrivacyKey.parse_privacyKeyAddedByPhone($0) }
|
||||||
dict[1343122938] = { return Api.PrivacyKey.parse_privacyKeyChatInvite($0) }
|
dict[1343122938] = { return Api.PrivacyKey.parse_privacyKeyChatInvite($0) }
|
||||||
dict[1777096355] = { return Api.PrivacyKey.parse_privacyKeyForwards($0) }
|
dict[1777096355] = { return Api.PrivacyKey.parse_privacyKeyForwards($0) }
|
||||||
@ -953,7 +954,7 @@ fileprivate let parsers: [Int32 : (BufferReader) -> Any?] = {
|
|||||||
dict[415997816] = { return Api.help.InviteText.parse_inviteText($0) }
|
dict[415997816] = { return Api.help.InviteText.parse_inviteText($0) }
|
||||||
dict[-1600596305] = { return Api.help.PassportConfig.parse_passportConfig($0) }
|
dict[-1600596305] = { return Api.help.PassportConfig.parse_passportConfig($0) }
|
||||||
dict[-1078332329] = { return Api.help.PassportConfig.parse_passportConfigNotModified($0) }
|
dict[-1078332329] = { return Api.help.PassportConfig.parse_passportConfigNotModified($0) }
|
||||||
dict[-1974518743] = { return Api.help.PremiumPromo.parse_premiumPromo($0) }
|
dict[1395946908] = { return Api.help.PremiumPromo.parse_premiumPromo($0) }
|
||||||
dict[-1942390465] = { return Api.help.PromoData.parse_promoData($0) }
|
dict[-1942390465] = { return Api.help.PromoData.parse_promoData($0) }
|
||||||
dict[-1728664459] = { return Api.help.PromoData.parse_promoDataEmpty($0) }
|
dict[-1728664459] = { return Api.help.PromoData.parse_promoDataEmpty($0) }
|
||||||
dict[235081943] = { return Api.help.RecentMeUrls.parse_recentMeUrls($0) }
|
dict[235081943] = { return Api.help.RecentMeUrls.parse_recentMeUrls($0) }
|
||||||
@ -1497,6 +1498,8 @@ public extension Api {
|
|||||||
_1.serialize(buffer, boxed)
|
_1.serialize(buffer, boxed)
|
||||||
case let _1 as Api.PremiumGiftOption:
|
case let _1 as Api.PremiumGiftOption:
|
||||||
_1.serialize(buffer, boxed)
|
_1.serialize(buffer, boxed)
|
||||||
|
case let _1 as Api.PremiumSubscriptionOption:
|
||||||
|
_1.serialize(buffer, boxed)
|
||||||
case let _1 as Api.PrivacyKey:
|
case let _1 as Api.PrivacyKey:
|
||||||
_1.serialize(buffer, boxed)
|
_1.serialize(buffer, boxed)
|
||||||
case let _1 as Api.PrivacyRule:
|
case let _1 as Api.PrivacyRule:
|
||||||
|
@ -652,6 +652,62 @@ public extension Api {
|
|||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
public extension Api {
|
||||||
|
enum PremiumSubscriptionOption: TypeConstructorDescription {
|
||||||
|
case premiumSubscriptionOption(flags: Int32, months: Int32, currency: String, amount: Int64, botUrl: String, storeProduct: String?)
|
||||||
|
|
||||||
|
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||||
|
switch self {
|
||||||
|
case .premiumSubscriptionOption(let flags, let months, let currency, let amount, let botUrl, let storeProduct):
|
||||||
|
if boxed {
|
||||||
|
buffer.appendInt32(-1225711938)
|
||||||
|
}
|
||||||
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
|
serializeInt32(months, buffer: buffer, boxed: false)
|
||||||
|
serializeString(currency, buffer: buffer, boxed: false)
|
||||||
|
serializeInt64(amount, buffer: buffer, boxed: false)
|
||||||
|
serializeString(botUrl, buffer: buffer, boxed: false)
|
||||||
|
if Int(flags) & Int(1 << 0) != 0 {serializeString(storeProduct!, buffer: buffer, boxed: false)}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||||
|
switch self {
|
||||||
|
case .premiumSubscriptionOption(let flags, let months, let currency, let amount, let botUrl, let storeProduct):
|
||||||
|
return ("premiumSubscriptionOption", [("flags", String(describing: flags)), ("months", String(describing: months)), ("currency", String(describing: currency)), ("amount", String(describing: amount)), ("botUrl", String(describing: botUrl)), ("storeProduct", String(describing: storeProduct))])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func parse_premiumSubscriptionOption(_ reader: BufferReader) -> PremiumSubscriptionOption? {
|
||||||
|
var _1: Int32?
|
||||||
|
_1 = reader.readInt32()
|
||||||
|
var _2: Int32?
|
||||||
|
_2 = reader.readInt32()
|
||||||
|
var _3: String?
|
||||||
|
_3 = parseString(reader)
|
||||||
|
var _4: Int64?
|
||||||
|
_4 = reader.readInt64()
|
||||||
|
var _5: String?
|
||||||
|
_5 = parseString(reader)
|
||||||
|
var _6: String?
|
||||||
|
if Int(_1!) & Int(1 << 0) != 0 {_6 = parseString(reader) }
|
||||||
|
let _c1 = _1 != nil
|
||||||
|
let _c2 = _2 != nil
|
||||||
|
let _c3 = _3 != nil
|
||||||
|
let _c4 = _4 != nil
|
||||||
|
let _c5 = _5 != nil
|
||||||
|
let _c6 = (Int(_1!) & Int(1 << 0) == 0) || _6 != nil
|
||||||
|
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
||||||
|
return Api.PremiumSubscriptionOption.premiumSubscriptionOption(flags: _1!, months: _2!, currency: _3!, amount: _4!, botUrl: _5!, storeProduct: _6)
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
public extension Api {
|
public extension Api {
|
||||||
enum PrivacyKey: TypeConstructorDescription {
|
enum PrivacyKey: TypeConstructorDescription {
|
||||||
case privacyKeyAddedByPhone
|
case privacyKeyAddedByPhone
|
||||||
|
@ -414,13 +414,13 @@ public extension Api.help {
|
|||||||
}
|
}
|
||||||
public extension Api.help {
|
public extension Api.help {
|
||||||
enum PremiumPromo: TypeConstructorDescription {
|
enum PremiumPromo: TypeConstructorDescription {
|
||||||
case premiumPromo(statusText: String, statusEntities: [Api.MessageEntity], videoSections: [String], videos: [Api.Document], currency: String, monthlyAmount: Int64, users: [Api.User])
|
case premiumPromo(statusText: String, statusEntities: [Api.MessageEntity], videoSections: [String], videos: [Api.Document], periodOptions: [Api.PremiumSubscriptionOption], users: [Api.User])
|
||||||
|
|
||||||
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
public func serialize(_ buffer: Buffer, _ boxed: Swift.Bool) {
|
||||||
switch self {
|
switch self {
|
||||||
case .premiumPromo(let statusText, let statusEntities, let videoSections, let videos, let currency, let monthlyAmount, let users):
|
case .premiumPromo(let statusText, let statusEntities, let videoSections, let videos, let periodOptions, let users):
|
||||||
if boxed {
|
if boxed {
|
||||||
buffer.appendInt32(-1974518743)
|
buffer.appendInt32(1395946908)
|
||||||
}
|
}
|
||||||
serializeString(statusText, buffer: buffer, boxed: false)
|
serializeString(statusText, buffer: buffer, boxed: false)
|
||||||
buffer.appendInt32(481674261)
|
buffer.appendInt32(481674261)
|
||||||
@ -438,8 +438,11 @@ public extension Api.help {
|
|||||||
for item in videos {
|
for item in videos {
|
||||||
item.serialize(buffer, true)
|
item.serialize(buffer, true)
|
||||||
}
|
}
|
||||||
serializeString(currency, buffer: buffer, boxed: false)
|
buffer.appendInt32(481674261)
|
||||||
serializeInt64(monthlyAmount, buffer: buffer, boxed: false)
|
buffer.appendInt32(Int32(periodOptions.count))
|
||||||
|
for item in periodOptions {
|
||||||
|
item.serialize(buffer, true)
|
||||||
|
}
|
||||||
buffer.appendInt32(481674261)
|
buffer.appendInt32(481674261)
|
||||||
buffer.appendInt32(Int32(users.count))
|
buffer.appendInt32(Int32(users.count))
|
||||||
for item in users {
|
for item in users {
|
||||||
@ -451,8 +454,8 @@ public extension Api.help {
|
|||||||
|
|
||||||
public func descriptionFields() -> (String, [(String, Any)]) {
|
public func descriptionFields() -> (String, [(String, Any)]) {
|
||||||
switch self {
|
switch self {
|
||||||
case .premiumPromo(let statusText, let statusEntities, let videoSections, let videos, let currency, let monthlyAmount, let users):
|
case .premiumPromo(let statusText, let statusEntities, let videoSections, let videos, let periodOptions, let users):
|
||||||
return ("premiumPromo", [("statusText", String(describing: statusText)), ("statusEntities", String(describing: statusEntities)), ("videoSections", String(describing: videoSections)), ("videos", String(describing: videos)), ("currency", String(describing: currency)), ("monthlyAmount", String(describing: monthlyAmount)), ("users", String(describing: users))])
|
return ("premiumPromo", [("statusText", String(describing: statusText)), ("statusEntities", String(describing: statusEntities)), ("videoSections", String(describing: videoSections)), ("videos", String(describing: videos)), ("periodOptions", String(describing: periodOptions)), ("users", String(describing: users))])
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -471,13 +474,13 @@ public extension Api.help {
|
|||||||
if let _ = reader.readInt32() {
|
if let _ = reader.readInt32() {
|
||||||
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self)
|
_4 = Api.parseVector(reader, elementSignature: 0, elementType: Api.Document.self)
|
||||||
}
|
}
|
||||||
var _5: String?
|
var _5: [Api.PremiumSubscriptionOption]?
|
||||||
_5 = parseString(reader)
|
|
||||||
var _6: Int64?
|
|
||||||
_6 = reader.readInt64()
|
|
||||||
var _7: [Api.User]?
|
|
||||||
if let _ = reader.readInt32() {
|
if let _ = reader.readInt32() {
|
||||||
_7 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
_5 = Api.parseVector(reader, elementSignature: 0, elementType: Api.PremiumSubscriptionOption.self)
|
||||||
|
}
|
||||||
|
var _6: [Api.User]?
|
||||||
|
if let _ = reader.readInt32() {
|
||||||
|
_6 = Api.parseVector(reader, elementSignature: 0, elementType: Api.User.self)
|
||||||
}
|
}
|
||||||
let _c1 = _1 != nil
|
let _c1 = _1 != nil
|
||||||
let _c2 = _2 != nil
|
let _c2 = _2 != nil
|
||||||
@ -485,9 +488,8 @@ public extension Api.help {
|
|||||||
let _c4 = _4 != nil
|
let _c4 = _4 != nil
|
||||||
let _c5 = _5 != nil
|
let _c5 = _5 != nil
|
||||||
let _c6 = _6 != nil
|
let _c6 = _6 != nil
|
||||||
let _c7 = _7 != nil
|
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 {
|
||||||
if _c1 && _c2 && _c3 && _c4 && _c5 && _c6 && _c7 {
|
return Api.help.PremiumPromo.premiumPromo(statusText: _1!, statusEntities: _2!, videoSections: _3!, videos: _4!, periodOptions: _5!, users: _6!)
|
||||||
return Api.help.PremiumPromo.premiumPromo(statusText: _1!, statusEntities: _2!, videoSections: _3!, videos: _4!, currency: _5!, monthlyAmount: _6!, users: _7!)
|
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -5383,13 +5383,13 @@ public extension Api.functions.messages {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
public extension Api.functions.messages {
|
public extension Api.functions.messages {
|
||||||
static func reportReaction(peer: Api.InputPeer, id: Int32, userId: Api.InputUser) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
static func reportReaction(peer: Api.InputPeer, id: Int32, reactionPeer: Api.InputPeer) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.Bool>) {
|
||||||
let buffer = Buffer()
|
let buffer = Buffer()
|
||||||
buffer.appendInt32(1631726152)
|
buffer.appendInt32(1063567478)
|
||||||
peer.serialize(buffer, true)
|
peer.serialize(buffer, true)
|
||||||
serializeInt32(id, buffer: buffer, boxed: false)
|
serializeInt32(id, buffer: buffer, boxed: false)
|
||||||
userId.serialize(buffer, true)
|
reactionPeer.serialize(buffer, true)
|
||||||
return (FunctionDescription(name: "messages.reportReaction", parameters: [("peer", String(describing: peer)), ("id", String(describing: id)), ("userId", String(describing: userId))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
|
return (FunctionDescription(name: "messages.reportReaction", parameters: [("peer", String(describing: peer)), ("id", String(describing: id)), ("reactionPeer", String(describing: reactionPeer))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.Bool? in
|
||||||
let reader = BufferReader(buffer)
|
let reader = BufferReader(buffer)
|
||||||
var result: Api.Bool?
|
var result: Api.Bool?
|
||||||
if let signature = reader.readInt32() {
|
if let signature = reader.readInt32() {
|
||||||
@ -5432,14 +5432,15 @@ public extension Api.functions.messages {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
public extension Api.functions.messages {
|
public extension Api.functions.messages {
|
||||||
static func requestSimpleWebView(flags: Int32, bot: Api.InputUser, url: String, themeParams: Api.DataJSON?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.SimpleWebViewResult>) {
|
static func requestSimpleWebView(flags: Int32, bot: Api.InputUser, url: String, themeParams: Api.DataJSON?, platform: String) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.SimpleWebViewResult>) {
|
||||||
let buffer = Buffer()
|
let buffer = Buffer()
|
||||||
buffer.appendInt32(1790652275)
|
buffer.appendInt32(698084494)
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
bot.serialize(buffer, true)
|
bot.serialize(buffer, true)
|
||||||
serializeString(url, buffer: buffer, boxed: false)
|
serializeString(url, buffer: buffer, boxed: false)
|
||||||
if Int(flags) & Int(1 << 0) != 0 {themeParams!.serialize(buffer, true)}
|
if Int(flags) & Int(1 << 0) != 0 {themeParams!.serialize(buffer, true)}
|
||||||
return (FunctionDescription(name: "messages.requestSimpleWebView", parameters: [("flags", String(describing: flags)), ("bot", String(describing: bot)), ("url", String(describing: url)), ("themeParams", String(describing: themeParams))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.SimpleWebViewResult? in
|
serializeString(platform, buffer: buffer, boxed: false)
|
||||||
|
return (FunctionDescription(name: "messages.requestSimpleWebView", parameters: [("flags", String(describing: flags)), ("bot", String(describing: bot)), ("url", String(describing: url)), ("themeParams", String(describing: themeParams)), ("platform", String(describing: platform))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.SimpleWebViewResult? in
|
||||||
let reader = BufferReader(buffer)
|
let reader = BufferReader(buffer)
|
||||||
var result: Api.SimpleWebViewResult?
|
var result: Api.SimpleWebViewResult?
|
||||||
if let signature = reader.readInt32() {
|
if let signature = reader.readInt32() {
|
||||||
@ -5469,18 +5470,19 @@ public extension Api.functions.messages {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
public extension Api.functions.messages {
|
public extension Api.functions.messages {
|
||||||
static func requestWebView(flags: Int32, peer: Api.InputPeer, bot: Api.InputUser, url: String?, startParam: String?, themeParams: Api.DataJSON?, replyToMsgId: Int32?, sendAs: Api.InputPeer?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.WebViewResult>) {
|
static func requestWebView(flags: Int32, peer: Api.InputPeer, bot: Api.InputUser, url: String?, startParam: String?, themeParams: Api.DataJSON?, platform: String, replyToMsgId: Int32?, sendAs: Api.InputPeer?) -> (FunctionDescription, Buffer, DeserializeFunctionResponse<Api.WebViewResult>) {
|
||||||
let buffer = Buffer()
|
let buffer = Buffer()
|
||||||
buffer.appendInt32(-1850648527)
|
buffer.appendInt32(-58219204)
|
||||||
serializeInt32(flags, buffer: buffer, boxed: false)
|
serializeInt32(flags, buffer: buffer, boxed: false)
|
||||||
peer.serialize(buffer, true)
|
peer.serialize(buffer, true)
|
||||||
bot.serialize(buffer, true)
|
bot.serialize(buffer, true)
|
||||||
if Int(flags) & Int(1 << 1) != 0 {serializeString(url!, buffer: buffer, boxed: false)}
|
if Int(flags) & Int(1 << 1) != 0 {serializeString(url!, buffer: buffer, boxed: false)}
|
||||||
if Int(flags) & Int(1 << 3) != 0 {serializeString(startParam!, buffer: buffer, boxed: false)}
|
if Int(flags) & Int(1 << 3) != 0 {serializeString(startParam!, buffer: buffer, boxed: false)}
|
||||||
if Int(flags) & Int(1 << 2) != 0 {themeParams!.serialize(buffer, true)}
|
if Int(flags) & Int(1 << 2) != 0 {themeParams!.serialize(buffer, true)}
|
||||||
|
serializeString(platform, buffer: buffer, boxed: false)
|
||||||
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(replyToMsgId!, buffer: buffer, boxed: false)}
|
if Int(flags) & Int(1 << 0) != 0 {serializeInt32(replyToMsgId!, buffer: buffer, boxed: false)}
|
||||||
if Int(flags) & Int(1 << 13) != 0 {sendAs!.serialize(buffer, true)}
|
if Int(flags) & Int(1 << 13) != 0 {sendAs!.serialize(buffer, true)}
|
||||||
return (FunctionDescription(name: "messages.requestWebView", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("bot", String(describing: bot)), ("url", String(describing: url)), ("startParam", String(describing: startParam)), ("themeParams", String(describing: themeParams)), ("replyToMsgId", String(describing: replyToMsgId)), ("sendAs", String(describing: sendAs))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.WebViewResult? in
|
return (FunctionDescription(name: "messages.requestWebView", parameters: [("flags", String(describing: flags)), ("peer", String(describing: peer)), ("bot", String(describing: bot)), ("url", String(describing: url)), ("startParam", String(describing: startParam)), ("themeParams", String(describing: themeParams)), ("platform", String(describing: platform)), ("replyToMsgId", String(describing: replyToMsgId)), ("sendAs", String(describing: sendAs))]), buffer, DeserializeFunctionResponse { (buffer: Buffer) -> Api.WebViewResult? in
|
||||||
let reader = BufferReader(buffer)
|
let reader = BufferReader(buffer)
|
||||||
var result: Api.WebViewResult?
|
var result: Api.WebViewResult?
|
||||||
if let signature = reader.readInt32() {
|
if let signature = reader.readInt32() {
|
||||||
|
@ -41,8 +41,7 @@ private func presentLiveLocationController(context: AccountContext, peerId: Peer
|
|||||||
}, callPeer: { _, _ in
|
}, callPeer: { _, _ in
|
||||||
}, enqueueMessage: { message in
|
}, enqueueMessage: { message in
|
||||||
let _ = enqueueMessages(account: context.account, peerId: peerId, messages: [message]).start()
|
let _ = enqueueMessages(account: context.account, peerId: peerId, messages: [message]).start()
|
||||||
}, sendSticker: nil,
|
}, sendSticker: nil, sendEmoji: nil, setupTemporaryHiddenMedia: { _, _, _ in
|
||||||
setupTemporaryHiddenMedia: { _, _, _ in
|
|
||||||
}, chatAvatarHiddenMedia: { _, _ in
|
}, chatAvatarHiddenMedia: { _, _ in
|
||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
@ -322,6 +322,31 @@ public enum AuthorizationEmailVerificationError {
|
|||||||
case timeout
|
case timeout
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct ChangeLoginEmailData: Equatable {
|
||||||
|
public let email: String
|
||||||
|
public let length: Int32
|
||||||
|
}
|
||||||
|
|
||||||
|
public func sendLoginEmailChangeCode(account: Account, email: String) -> Signal<ChangeLoginEmailData, AuthorizationSendEmailCodeError> {
|
||||||
|
return account.network.request(Api.functions.account.sendVerifyEmailCode(purpose: .emailVerifyPurposeLoginChange, email: email), automaticFloodWait: false)
|
||||||
|
|> `catch` { error -> Signal<Api.account.SentEmailCode, AuthorizationSendEmailCodeError> in
|
||||||
|
let errorDescription = error.errorDescription ?? ""
|
||||||
|
if errorDescription.hasPrefix("FLOOD_WAIT") {
|
||||||
|
return .fail(.limitExceeded)
|
||||||
|
} else if errorDescription == "CODE_HASH_EXPIRED" || errorDescription == "PHONE_CODE_EXPIRED" {
|
||||||
|
return .fail(.codeExpired)
|
||||||
|
} else {
|
||||||
|
return .fail(.generic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|> map { result -> ChangeLoginEmailData in
|
||||||
|
switch result {
|
||||||
|
case let .sentEmailCode(_, length):
|
||||||
|
return ChangeLoginEmailData(email: email, length: length)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public func sendLoginEmailCode(account: UnauthorizedAccount, email: String) -> Signal<Never, AuthorizationSendEmailCodeError> {
|
public func sendLoginEmailCode(account: UnauthorizedAccount, email: String) -> Signal<Never, AuthorizationSendEmailCodeError> {
|
||||||
return account.postbox.transaction { transaction -> Signal<Never, AuthorizationSendEmailCodeError> in
|
return account.postbox.transaction { transaction -> Signal<Never, AuthorizationSendEmailCodeError> in
|
||||||
if let state = transaction.getState() as? UnauthorizedAccountState {
|
if let state = transaction.getState() as? UnauthorizedAccountState {
|
||||||
|
@ -19,7 +19,7 @@ func updatePremiumPromoConfigurationOnce(postbox: Postbox, network: Network) ->
|
|||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
return postbox.transaction { transaction -> Void in
|
return postbox.transaction { transaction -> Void in
|
||||||
if case let .premiumPromo(_, _, _, _, _, _, apiUsers) = result {
|
if case let .premiumPromo(_, _, _, _, _, apiUsers) = result {
|
||||||
let users = apiUsers.map { TelegramUser(user: $0) }
|
let users = apiUsers.map { TelegramUser(user: $0) }
|
||||||
updatePeers(transaction: transaction, peers: users, update: { current, updated -> Peer in
|
updatePeers(transaction: transaction, peers: users, update: { current, updated -> Peer in
|
||||||
if let updated = updated as? TelegramUser {
|
if let updated = updated as? TelegramUser {
|
||||||
@ -65,11 +65,10 @@ private func updatePremiumPromoConfiguration(transaction: Transaction, _ f: (Pre
|
|||||||
private extension PremiumPromoConfiguration {
|
private extension PremiumPromoConfiguration {
|
||||||
init(apiPremiumPromo: Api.help.PremiumPromo) {
|
init(apiPremiumPromo: Api.help.PremiumPromo) {
|
||||||
switch apiPremiumPromo {
|
switch apiPremiumPromo {
|
||||||
case let .premiumPromo(statusText, statusEntities, videoSections, videoFiles, currency, monthlyAmount, _):
|
case let .premiumPromo(statusText, statusEntities, videoSections, videoFiles, options, _):
|
||||||
self.status = statusText
|
self.status = statusText
|
||||||
self.statusEntities = messageTextEntitiesFromApiEntities(statusEntities)
|
self.statusEntities = messageTextEntitiesFromApiEntities(statusEntities)
|
||||||
self.currency = currency
|
|
||||||
self.monthlyAmount = monthlyAmount
|
|
||||||
var videos: [String: TelegramMediaFile] = [:]
|
var videos: [String: TelegramMediaFile] = [:]
|
||||||
for (key, document) in zip(videoSections, videoFiles) {
|
for (key, document) in zip(videoSections, videoFiles) {
|
||||||
if let file = telegramMediaFileFromApiDocument(document) {
|
if let file = telegramMediaFileFromApiDocument(document) {
|
||||||
@ -77,6 +76,14 @@ private extension PremiumPromoConfiguration {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
self.videos = videos
|
self.videos = videos
|
||||||
|
|
||||||
|
var productOptions: [PremiumProductOption] = []
|
||||||
|
for option in options {
|
||||||
|
if case let .premiumSubscriptionOption(_, months, currency, amount, botUrl, storeProduct) = option {
|
||||||
|
productOptions.append(PremiumProductOption(months: months, currency: currency, amount: amount, botUrl: botUrl, storeProductId: storeProduct))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.premiumProductOptions = productOptions
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,10 +4,9 @@ import Postbox
|
|||||||
public struct PremiumPromoConfiguration: Codable, Equatable {
|
public struct PremiumPromoConfiguration: Codable, Equatable {
|
||||||
enum CodingKeys: String, CodingKey {
|
enum CodingKeys: String, CodingKey {
|
||||||
case status
|
case status
|
||||||
case currency
|
|
||||||
case monthlyAmount
|
|
||||||
case statusEntities
|
case statusEntities
|
||||||
case videos
|
case videos
|
||||||
|
case premiumProductOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct DictionaryPair: Codable {
|
private struct DictionaryPair: Codable {
|
||||||
@ -34,23 +33,56 @@ public struct PremiumPromoConfiguration: Codable, Equatable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public struct PremiumProductOption: Codable, Equatable {
|
||||||
|
public let months: Int32
|
||||||
|
public let currency: String
|
||||||
|
public let amount: Int64
|
||||||
|
public let botUrl: String
|
||||||
|
public let storeProductId: String?
|
||||||
|
|
||||||
|
public init(months: Int32, currency: String, amount: Int64, botUrl: String, storeProductId: String?) {
|
||||||
|
self.months = months
|
||||||
|
self.currency = currency
|
||||||
|
self.amount = amount
|
||||||
|
self.botUrl = botUrl
|
||||||
|
self.storeProductId = storeProductId
|
||||||
|
}
|
||||||
|
|
||||||
|
public init(decoder: PostboxDecoder) {
|
||||||
|
self.months = decoder.decodeInt32ForKey("months", orElse: 0)
|
||||||
|
self.currency = decoder.decodeStringForKey("currency", orElse: "")
|
||||||
|
self.amount = decoder.decodeInt64ForKey("amount", orElse: 0)
|
||||||
|
self.botUrl = decoder.decodeStringForKey("botUrl", orElse: "")
|
||||||
|
self.storeProductId = decoder.decodeOptionalStringForKey("storeProductId")
|
||||||
|
}
|
||||||
|
|
||||||
|
public func encode(_ encoder: PostboxEncoder) {
|
||||||
|
encoder.encodeInt32(self.months, forKey: "months")
|
||||||
|
encoder.encodeString(self.currency, forKey: "currency")
|
||||||
|
encoder.encodeInt64(self.amount, forKey: "amount")
|
||||||
|
encoder.encodeString(self.botUrl, forKey: "botUrl")
|
||||||
|
if let storeProductId = self.storeProductId {
|
||||||
|
encoder.encodeString(storeProductId, forKey: "storeProductId")
|
||||||
|
} else {
|
||||||
|
encoder.encodeNil(forKey: "storeProductId")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public var status: String
|
public var status: String
|
||||||
public var statusEntities: [MessageTextEntity]
|
public var statusEntities: [MessageTextEntity]
|
||||||
public var videos: [String: TelegramMediaFile]
|
public var videos: [String: TelegramMediaFile]
|
||||||
|
public var premiumProductOptions: [PremiumProductOption]
|
||||||
public var currency: String
|
|
||||||
public var monthlyAmount: Int64
|
|
||||||
|
|
||||||
public static var defaultValue: PremiumPromoConfiguration {
|
public static var defaultValue: PremiumPromoConfiguration {
|
||||||
return PremiumPromoConfiguration(status: "", statusEntities: [], videos: [:], currency: "", monthlyAmount: 0)
|
return PremiumPromoConfiguration(status: "", statusEntities: [], videos: [:], premiumProductOptions: [])
|
||||||
}
|
}
|
||||||
|
|
||||||
init(status: String, statusEntities: [MessageTextEntity], videos: [String: TelegramMediaFile], currency: String, monthlyAmount: Int64) {
|
init(status: String, statusEntities: [MessageTextEntity], videos: [String: TelegramMediaFile], premiumProductOptions: [PremiumProductOption]) {
|
||||||
self.status = status
|
self.status = status
|
||||||
self.currency = currency
|
|
||||||
self.monthlyAmount = monthlyAmount
|
|
||||||
self.statusEntities = statusEntities
|
self.statusEntities = statusEntities
|
||||||
self.videos = videos
|
self.videos = videos
|
||||||
|
self.premiumProductOptions = premiumProductOptions
|
||||||
}
|
}
|
||||||
|
|
||||||
public init(from decoder: Decoder) throws {
|
public init(from decoder: Decoder) throws {
|
||||||
@ -66,9 +98,7 @@ public struct PremiumPromoConfiguration: Codable, Equatable {
|
|||||||
}
|
}
|
||||||
self.videos = videos
|
self.videos = videos
|
||||||
|
|
||||||
self.currency = try container.decode(String.self, forKey: .currency)
|
self.premiumProductOptions = try container.decode([PremiumProductOption].self, forKey: .premiumProductOptions)
|
||||||
self.monthlyAmount = try container.decode(Int64.self, forKey: .monthlyAmount)
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public func encode(to encoder: Encoder) throws {
|
public func encode(to encoder: Encoder) throws {
|
||||||
@ -76,13 +106,13 @@ public struct PremiumPromoConfiguration: Codable, Equatable {
|
|||||||
|
|
||||||
try container.encode(self.status, forKey: .status)
|
try container.encode(self.status, forKey: .status)
|
||||||
try container.encode(self.statusEntities, forKey: .statusEntities)
|
try container.encode(self.statusEntities, forKey: .statusEntities)
|
||||||
try container.encode(self.currency, forKey: .currency)
|
|
||||||
try container.encode(self.monthlyAmount, forKey: .monthlyAmount)
|
|
||||||
|
|
||||||
var pairs: [DictionaryPair] = []
|
var pairs: [DictionaryPair] = []
|
||||||
for (key, file) in self.videos {
|
for (key, file) in self.videos {
|
||||||
pairs.append(DictionaryPair(key, value: file))
|
pairs.append(DictionaryPair(key, value: file))
|
||||||
}
|
}
|
||||||
try container.encode(pairs, forKey: .videos)
|
try container.encode(pairs, forKey: .videos)
|
||||||
|
|
||||||
|
try container.encode(self.premiumProductOptions, forKey: .premiumProductOptions)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -4,6 +4,12 @@ import SwiftSignalKit
|
|||||||
import TelegramApi
|
import TelegramApi
|
||||||
import MtProtoKit
|
import MtProtoKit
|
||||||
|
|
||||||
|
#if os(macOS)
|
||||||
|
private let botWebViewPlatform = "macos"
|
||||||
|
#else
|
||||||
|
private let botWebViewPlatform = "ios"
|
||||||
|
#endif
|
||||||
|
|
||||||
public enum RequestSimpleWebViewError {
|
public enum RequestSimpleWebViewError {
|
||||||
case generic
|
case generic
|
||||||
}
|
}
|
||||||
@ -22,7 +28,7 @@ func _internal_requestSimpleWebView(postbox: Postbox, network: Network, botId: P
|
|||||||
if let _ = serializedThemeParams {
|
if let _ = serializedThemeParams {
|
||||||
flags |= (1 << 0)
|
flags |= (1 << 0)
|
||||||
}
|
}
|
||||||
return network.request(Api.functions.messages.requestSimpleWebView(flags: flags, bot: inputUser, url: url, themeParams: serializedThemeParams))
|
return network.request(Api.functions.messages.requestSimpleWebView(flags: flags, bot: inputUser, url: url, themeParams: serializedThemeParams, platform: botWebViewPlatform))
|
||||||
|> mapError { _ -> RequestSimpleWebViewError in
|
|> mapError { _ -> RequestSimpleWebViewError in
|
||||||
return .generic
|
return .generic
|
||||||
}
|
}
|
||||||
@ -125,7 +131,7 @@ func _internal_requestWebView(postbox: Postbox, network: Network, stateManager:
|
|||||||
// if _ {
|
// if _ {
|
||||||
// flags |= (1 << 13)
|
// flags |= (1 << 13)
|
||||||
// }
|
// }
|
||||||
return network.request(Api.functions.messages.requestWebView(flags: flags, peer: inputPeer, bot: inputBot, url: url, startParam: payload, themeParams: serializedThemeParams, replyToMsgId: replyToMsgId, sendAs: nil))
|
return network.request(Api.functions.messages.requestWebView(flags: flags, peer: inputPeer, bot: inputBot, url: url, startParam: payload, themeParams: serializedThemeParams, platform: botWebViewPlatform, replyToMsgId: replyToMsgId, sendAs: nil))
|
||||||
|> mapError { _ -> RequestWebViewError in
|
|> mapError { _ -> RequestWebViewError in
|
||||||
return .generic
|
return .generic
|
||||||
}
|
}
|
||||||
|
@ -171,11 +171,11 @@ func _internal_reportPeerMessages(account: Account, messageIds: [MessageId], rea
|
|||||||
}
|
}
|
||||||
|
|
||||||
func _internal_reportPeerReaction(account: Account, authorId: PeerId, messageId: MessageId) -> Signal<Never, NoError> {
|
func _internal_reportPeerReaction(account: Account, authorId: PeerId, messageId: MessageId) -> Signal<Never, NoError> {
|
||||||
return account.postbox.transaction { transaction -> (Api.InputPeer, Api.InputUser)? in
|
return account.postbox.transaction { transaction -> (Api.InputPeer, Api.InputPeer)? in
|
||||||
guard let peer = transaction.getPeer(messageId.peerId).flatMap(apiInputPeer) else {
|
guard let peer = transaction.getPeer(messageId.peerId).flatMap(apiInputPeer) else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
guard let author = transaction.getPeer(authorId).flatMap(apiInputUser) else {
|
guard let author = transaction.getPeer(authorId).flatMap(apiInputPeer) else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return (peer, author)
|
return (peer, author)
|
||||||
@ -184,7 +184,7 @@ func _internal_reportPeerReaction(account: Account, authorId: PeerId, messageId:
|
|||||||
guard let (inputPeer, inputUser) = inputData else {
|
guard let (inputPeer, inputUser) = inputData else {
|
||||||
return .complete()
|
return .complete()
|
||||||
}
|
}
|
||||||
return account.network.request(Api.functions.messages.reportReaction(peer: inputPeer, id: messageId.id, userId: inputUser))
|
return account.network.request(Api.functions.messages.reportReaction(peer: inputPeer, id: messageId.id, reactionPeer: inputUser))
|
||||||
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
|> `catch` { _ -> Signal<Api.Bool, NoError> in
|
||||||
return .single(.boolFalse)
|
return .single(.boolFalse)
|
||||||
}
|
}
|
||||||
|
@ -163,6 +163,7 @@ private enum ApplicationSpecificGlobalNotice: Int32 {
|
|||||||
case sharedMediaScrollingTooltip = 29
|
case sharedMediaScrollingTooltip = 29
|
||||||
case sharedMediaFastScrollingTooltip = 30
|
case sharedMediaFastScrollingTooltip = 30
|
||||||
case forcedPasswordSetup = 31
|
case forcedPasswordSetup = 31
|
||||||
|
case emojiTooltip = 32
|
||||||
|
|
||||||
var key: ValueBoxKey {
|
var key: ValueBoxKey {
|
||||||
let v = ValueBoxKey(length: 4)
|
let v = ValueBoxKey(length: 4)
|
||||||
@ -339,6 +340,10 @@ private struct ApplicationSpecificNoticeKeys {
|
|||||||
static func sharedMediaFastScrollingTooltip() -> NoticeEntryKey {
|
static func sharedMediaFastScrollingTooltip() -> NoticeEntryKey {
|
||||||
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.sharedMediaFastScrollingTooltip.key)
|
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.sharedMediaFastScrollingTooltip.key)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static func emojiTooltip() -> NoticeEntryKey {
|
||||||
|
return NoticeEntryKey(namespace: noticeNamespace(namespace: globalNamespace), key: ApplicationSpecificGlobalNotice.emojiTooltip.key)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct ApplicationSpecificNotice {
|
public struct ApplicationSpecificNotice {
|
||||||
@ -953,6 +958,30 @@ public struct ApplicationSpecificNotice {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public static func getEmojiTooltip(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<Int32, NoError> {
|
||||||
|
return accountManager.transaction { transaction -> Int32 in
|
||||||
|
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.emojiTooltip())?.get(ApplicationSpecificCounterNotice.self) {
|
||||||
|
return value.value
|
||||||
|
} else {
|
||||||
|
return 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public static func incrementEmojiTooltip(accountManager: AccountManager<TelegramAccountManagerTypes>, count: Int32 = 1) -> Signal<Void, NoError> {
|
||||||
|
return accountManager.transaction { transaction -> Void in
|
||||||
|
var currentValue: Int32 = 0
|
||||||
|
if let value = transaction.getNotice(ApplicationSpecificNoticeKeys.emojiTooltip())?.get(ApplicationSpecificCounterNotice.self) {
|
||||||
|
currentValue = value.value
|
||||||
|
}
|
||||||
|
currentValue += count
|
||||||
|
|
||||||
|
if let entry = CodableEntry(ApplicationSpecificCounterNotice(value: currentValue)) {
|
||||||
|
transaction.setNotice(ApplicationSpecificNoticeKeys.emojiTooltip(), entry)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public static func dismissedTrendingStickerPacks(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<[Int64]?, NoError> {
|
public static func dismissedTrendingStickerPacks(accountManager: AccountManager<TelegramAccountManagerTypes>) -> Signal<[Int64]?, NoError> {
|
||||||
return accountManager.noticeEntry(key: ApplicationSpecificNoticeKeys.dismissedTrendingStickerPacks())
|
return accountManager.noticeEntry(key: ApplicationSpecificNoticeKeys.dismissedTrendingStickerPacks())
|
||||||
|> map { view -> [Int64]? in
|
|> map { view -> [Int64]? in
|
||||||
|
@ -2160,7 +2160,7 @@ public final class EmojiPagerContentComponent: Component {
|
|||||||
case featured
|
case featured
|
||||||
}
|
}
|
||||||
|
|
||||||
let item: Item
|
public let item: Item
|
||||||
|
|
||||||
private let content: ItemContent
|
private let content: ItemContent
|
||||||
private let placeholderColor: UIColor
|
private let placeholderColor: UIColor
|
||||||
|
12
submodules/TelegramUI/Images.xcassets/Settings/Menu/LoginEmail.imageset/Contents.json
vendored
Normal file
12
submodules/TelegramUI/Images.xcassets/Settings/Menu/LoginEmail.imageset/Contents.json
vendored
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"images" : [
|
||||||
|
{
|
||||||
|
"filename" : "LoginEmail.pdf",
|
||||||
|
"idiom" : "universal"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"info" : {
|
||||||
|
"author" : "xcode",
|
||||||
|
"version" : 1
|
||||||
|
}
|
||||||
|
}
|
122
submodules/TelegramUI/Images.xcassets/Settings/Menu/LoginEmail.imageset/LoginEmail.pdf
vendored
Normal file
122
submodules/TelegramUI/Images.xcassets/Settings/Menu/LoginEmail.imageset/LoginEmail.pdf
vendored
Normal file
@ -0,0 +1,122 @@
|
|||||||
|
%PDF-1.7
|
||||||
|
|
||||||
|
1 0 obj
|
||||||
|
<< >>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
2 0 obj
|
||||||
|
<< /Length 3 0 R >>
|
||||||
|
stream
|
||||||
|
/DeviceRGB CS
|
||||||
|
/DeviceRGB cs
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 0.000000 0.000000 cm
|
||||||
|
0.345098 0.337255 0.839216 scn
|
||||||
|
0.000000 18.799999 m
|
||||||
|
0.000000 22.720367 0.000000 24.680552 0.762954 26.177933 c
|
||||||
|
1.434068 27.495068 2.504932 28.565931 3.822066 29.237045 c
|
||||||
|
5.319448 30.000000 7.279633 30.000000 11.200000 30.000000 c
|
||||||
|
18.799999 30.000000 l
|
||||||
|
22.720367 30.000000 24.680552 30.000000 26.177933 29.237045 c
|
||||||
|
27.495068 28.565931 28.565931 27.495068 29.237045 26.177933 c
|
||||||
|
30.000000 24.680552 30.000000 22.720367 30.000000 18.799999 c
|
||||||
|
30.000000 11.200001 l
|
||||||
|
30.000000 7.279633 30.000000 5.319448 29.237045 3.822067 c
|
||||||
|
28.565931 2.504932 27.495068 1.434069 26.177933 0.762955 c
|
||||||
|
24.680552 0.000000 22.720367 0.000000 18.799999 0.000000 c
|
||||||
|
11.200000 0.000000 l
|
||||||
|
7.279633 0.000000 5.319448 0.000000 3.822066 0.762955 c
|
||||||
|
2.504932 1.434069 1.434068 2.504932 0.762954 3.822067 c
|
||||||
|
0.000000 5.319448 0.000000 7.279633 0.000000 11.200001 c
|
||||||
|
0.000000 18.799999 l
|
||||||
|
h
|
||||||
|
f
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
q
|
||||||
|
1.000000 0.000000 -0.000000 1.000000 5.201416 5.201416 cm
|
||||||
|
1.000000 1.000000 1.000000 scn
|
||||||
|
0.000000 9.798596 m
|
||||||
|
0.000000 15.210198 4.386970 19.597168 9.798572 19.597168 c
|
||||||
|
15.210174 19.597168 19.597141 15.210198 19.597141 9.798596 c
|
||||||
|
19.597141 7.536692 l
|
||||||
|
19.597141 5.872736 18.248240 4.523834 16.584286 4.523834 c
|
||||||
|
15.334453 4.523834 14.262368 5.284863 13.806146 6.368805 c
|
||||||
|
12.838721 5.239468 11.402221 4.523834 9.798572 4.523834 c
|
||||||
|
6.885401 4.523834 4.523810 6.885427 4.523810 9.798597 c
|
||||||
|
4.523810 12.711767 6.885401 15.073359 9.798572 15.073359 c
|
||||||
|
11.278581 15.073359 12.616220 14.463821 13.574155 13.482087 c
|
||||||
|
13.606840 13.866659 13.929350 14.168596 14.322381 14.168596 c
|
||||||
|
14.737121 14.168596 15.073334 13.832384 15.073334 13.417645 c
|
||||||
|
15.073334 9.798597 l
|
||||||
|
15.073334 7.536693 l
|
||||||
|
15.073334 6.702216 15.749810 6.025740 16.584286 6.025740 c
|
||||||
|
17.418760 6.025740 18.095238 6.702215 18.095238 7.536692 c
|
||||||
|
18.095238 9.798596 l
|
||||||
|
18.095238 14.380720 14.380694 18.095263 9.798572 18.095263 c
|
||||||
|
5.216448 18.095263 1.501905 14.380720 1.501905 9.798596 c
|
||||||
|
1.501905 9.710638 1.503271 9.623017 1.505982 9.535753 c
|
||||||
|
1.644797 5.075261 5.304389 1.501930 9.798572 1.501930 c
|
||||||
|
11.100951 1.501930 12.331239 1.801481 13.426208 2.334747 c
|
||||||
|
13.799078 2.516340 14.248561 2.361280 14.430154 1.988409 c
|
||||||
|
14.611748 1.615540 14.456687 1.166056 14.083817 0.984463 c
|
||||||
|
12.788459 0.353603 11.333742 0.000027 9.798572 0.000027 c
|
||||||
|
4.386970 0.000027 0.000000 4.386994 0.000000 9.798596 c
|
||||||
|
h
|
||||||
|
9.798572 13.571454 m
|
||||||
|
11.882263 13.571454 13.571428 11.882288 13.571428 9.798597 c
|
||||||
|
13.571428 7.714905 11.882263 6.025740 9.798572 6.025740 c
|
||||||
|
7.714880 6.025740 6.025714 7.714905 6.025714 9.798597 c
|
||||||
|
6.025714 11.882288 7.714880 13.571454 9.798572 13.571454 c
|
||||||
|
h
|
||||||
|
f*
|
||||||
|
n
|
||||||
|
Q
|
||||||
|
|
||||||
|
endstream
|
||||||
|
endobj
|
||||||
|
|
||||||
|
3 0 obj
|
||||||
|
2642
|
||||||
|
endobj
|
||||||
|
|
||||||
|
4 0 obj
|
||||||
|
<< /Annots []
|
||||||
|
/Type /Page
|
||||||
|
/MediaBox [ 0.000000 0.000000 30.000000 30.000000 ]
|
||||||
|
/Resources 1 0 R
|
||||||
|
/Contents 2 0 R
|
||||||
|
/Parent 5 0 R
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
5 0 obj
|
||||||
|
<< /Kids [ 4 0 R ]
|
||||||
|
/Count 1
|
||||||
|
/Type /Pages
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
6 0 obj
|
||||||
|
<< /Pages 5 0 R
|
||||||
|
/Type /Catalog
|
||||||
|
>>
|
||||||
|
endobj
|
||||||
|
|
||||||
|
xref
|
||||||
|
0 7
|
||||||
|
0000000000 65535 f
|
||||||
|
0000000010 00000 n
|
||||||
|
0000000034 00000 n
|
||||||
|
0000002732 00000 n
|
||||||
|
0000002755 00000 n
|
||||||
|
0000002928 00000 n
|
||||||
|
0000003002 00000 n
|
||||||
|
trailer
|
||||||
|
<< /ID [ (some) (id) ]
|
||||||
|
/Root 6 0 R
|
||||||
|
/Size 7
|
||||||
|
>>
|
||||||
|
startxref
|
||||||
|
3061
|
||||||
|
%%EOF
|
@ -25,6 +25,7 @@ import AppLock
|
|||||||
import AccountUtils
|
import AccountUtils
|
||||||
import ContextUI
|
import ContextUI
|
||||||
import TelegramCallsUI
|
import TelegramCallsUI
|
||||||
|
import AuthorizationUI
|
||||||
|
|
||||||
final class UnauthorizedApplicationContext {
|
final class UnauthorizedApplicationContext {
|
||||||
let sharedContext: SharedAccountContextImpl
|
let sharedContext: SharedAccountContextImpl
|
||||||
@ -48,6 +49,7 @@ final class UnauthorizedApplicationContext {
|
|||||||
self.rootController = AuthorizationSequenceController(sharedContext: sharedContext, account: account, otherAccountPhoneNumbers: otherAccountPhoneNumbers, presentationData: presentationData, openUrl: sharedContext.applicationBindings.openUrl, apiId: apiId, apiHash: apiHash, authorizationCompleted: {
|
self.rootController = AuthorizationSequenceController(sharedContext: sharedContext, account: account, otherAccountPhoneNumbers: otherAccountPhoneNumbers, presentationData: presentationData, openUrl: sharedContext.applicationBindings.openUrl, apiId: apiId, apiHash: apiHash, authorizationCompleted: {
|
||||||
authorizationCompleted?()
|
authorizationCompleted?()
|
||||||
})
|
})
|
||||||
|
(self.rootController as NavigationController).statusBarHost = sharedContext.mainWindow?.statusBarHost
|
||||||
|
|
||||||
authorizationCompleted = { [weak self] in
|
authorizationCompleted = { [weak self] in
|
||||||
self?.authorizationCompleted = true
|
self?.authorizationCompleted = true
|
||||||
|
@ -407,6 +407,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
private var raiseToListen: RaiseToListenManager?
|
private var raiseToListen: RaiseToListenManager?
|
||||||
private var voicePlaylistDidEndTimestamp: Double = 0.0
|
private var voicePlaylistDidEndTimestamp: Double = 0.0
|
||||||
|
|
||||||
|
private weak var emojiTooltipController: TooltipController?
|
||||||
private weak var sendingOptionsTooltipController: TooltipController?
|
private weak var sendingOptionsTooltipController: TooltipController?
|
||||||
private weak var searchResultsTooltipController: TooltipController?
|
private weak var searchResultsTooltipController: TooltipController?
|
||||||
private weak var messageTooltipController: TooltipController?
|
private weak var messageTooltipController: TooltipController?
|
||||||
@ -828,6 +829,8 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
self?.sendMessages([message])
|
self?.sendMessages([message])
|
||||||
}, sendSticker: canSendMessagesToChat(strongSelf.presentationInterfaceState) ? { fileReference, sourceNode, sourceRect in
|
}, sendSticker: canSendMessagesToChat(strongSelf.presentationInterfaceState) ? { fileReference, sourceNode, sourceRect in
|
||||||
return self?.controllerInteraction?.sendSticker(fileReference, false, false, nil, false, sourceNode, sourceRect, nil) ?? false
|
return self?.controllerInteraction?.sendSticker(fileReference, false, false, nil, false, sourceNode, sourceRect, nil) ?? false
|
||||||
|
} : nil, sendEmoji: canSendMessagesToChat(strongSelf.presentationInterfaceState) ? { text, attribute in
|
||||||
|
self?.controllerInteraction?.sendEmoji(text, attribute)
|
||||||
} : nil, setupTemporaryHiddenMedia: { signal, centralIndex, galleryMedia in
|
} : nil, setupTemporaryHiddenMedia: { signal, centralIndex, galleryMedia in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.temporaryHiddenGalleryMediaDisposable.set((signal |> deliverOnMainQueue).start(next: { entry in
|
strongSelf.temporaryHiddenGalleryMediaDisposable.set((signal |> deliverOnMainQueue).start(next: { entry in
|
||||||
@ -1949,6 +1952,29 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
strongSelf.sendMessages(transformedMessages)
|
strongSelf.sendMessages(transformedMessages)
|
||||||
}
|
}
|
||||||
return true
|
return true
|
||||||
|
}, sendEmoji: { [weak self] text, attribute in
|
||||||
|
if let strongSelf = self {
|
||||||
|
strongSelf.interfaceInteraction?.insertText(NSAttributedString(string: text, attributes: [ChatTextInputAttributes.customEmoji: attribute]))
|
||||||
|
strongSelf.updateChatPresentationInterfaceState(interactive: true, { state in
|
||||||
|
return state.updatedInputMode({ _ in
|
||||||
|
return .text
|
||||||
|
})
|
||||||
|
})
|
||||||
|
|
||||||
|
let _ = (ApplicationSpecificNotice.getEmojiTooltip(accountManager: strongSelf.context.sharedContext.accountManager)
|
||||||
|
|> deliverOnMainQueue).start(next: { count in
|
||||||
|
guard let strongSelf = self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if count < 2 {
|
||||||
|
let _ = ApplicationSpecificNotice.incrementEmojiTooltip(accountManager: strongSelf.context.sharedContext.accountManager).start()
|
||||||
|
|
||||||
|
Queue.mainQueue().after(0.5, {
|
||||||
|
strongSelf.displayEmojiTooltip()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
}, sendGif: { [weak self] fileReference, sourceView, sourceRect, silentPosting, schedule in
|
}, sendGif: { [weak self] fileReference, sourceView, sourceRect, silentPosting, schedule in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
if let _ = strongSelf.presentationInterfaceState.slowmodeState, strongSelf.presentationInterfaceState.subject != .scheduledMessages {
|
if let _ = strongSelf.presentationInterfaceState.slowmodeState, strongSelf.presentationInterfaceState.subject != .scheduledMessages {
|
||||||
@ -15953,6 +15979,26 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}))
|
}))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func displayEmojiTooltip() {
|
||||||
|
guard let rect = self.chatDisplayNode.frameForEmojiButton(), self.effectiveNavigationController?.topViewController === self else {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
self.emojiTooltipController?.dismiss()
|
||||||
|
let tooltipController = TooltipController(content: .text(self.presentationData.strings.Conversation_EmojiTooltip), baseFontSize: self.presentationData.listsFontSize.baseDisplaySize, timeout: 3.0, dismissByTapOutside: true, dismissImmediatelyOnLayoutUpdate: true, padding: 2.0)
|
||||||
|
self.emojiTooltipController = tooltipController
|
||||||
|
tooltipController.dismissed = { [weak self, weak tooltipController] _ in
|
||||||
|
if let strongSelf = self, let tooltipController = tooltipController, strongSelf.emojiTooltipController === tooltipController {
|
||||||
|
strongSelf.emojiTooltipController = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
self.present(tooltipController, in: .window(.root), with: TooltipControllerPresentationArguments(sourceNodeAndRect: { [weak self] in
|
||||||
|
if let strongSelf = self {
|
||||||
|
return (strongSelf.chatDisplayNode, rect.offsetBy(dx: 0.0, dy: -3.0))
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}))
|
||||||
|
}
|
||||||
|
|
||||||
private func displayChecksTooltip() {
|
private func displayChecksTooltip() {
|
||||||
self.checksTooltipController?.dismiss()
|
self.checksTooltipController?.dismiss()
|
||||||
|
|
||||||
@ -15993,6 +16039,7 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func dismissAllTooltips() {
|
private func dismissAllTooltips() {
|
||||||
|
self.emojiTooltipController?.dismiss()
|
||||||
self.sendingOptionsTooltipController?.dismiss()
|
self.sendingOptionsTooltipController?.dismiss()
|
||||||
self.searchResultsTooltipController?.dismiss()
|
self.searchResultsTooltipController?.dismiss()
|
||||||
self.messageTooltipController?.dismiss()
|
self.messageTooltipController?.dismiss()
|
||||||
@ -16750,7 +16797,11 @@ public final class ChatControllerImpl: TelegramBaseController, ChatController, G
|
|||||||
self.chatDisplayNode.dismissTextInput()
|
self.chatDisplayNode.dismissTextInput()
|
||||||
|
|
||||||
let presentationData = self.presentationData
|
let presentationData = self.presentationData
|
||||||
let controller = StickerPackScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, mainStickerPack: packReference, stickerPacks: Array(references), parentNavigationController: self.effectiveNavigationController, actionPerformed: { [weak self] actions in
|
let controller = StickerPackScreen(context: self.context, updatedPresentationData: self.updatedPresentationData, mainStickerPack: packReference, stickerPacks: Array(references), parentNavigationController: self.effectiveNavigationController, sendEmoji: canSendMessagesToChat(self.presentationInterfaceState) ? { [weak self] text, attribute in
|
||||||
|
if let strongSelf = self {
|
||||||
|
strongSelf.controllerInteraction?.sendEmoji(text, attribute)
|
||||||
|
}
|
||||||
|
} : nil, actionPerformed: { [weak self] actions in
|
||||||
guard let strongSelf = self else {
|
guard let strongSelf = self else {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
@ -13,6 +13,7 @@ import ChatInterfaceState
|
|||||||
import UndoUI
|
import UndoUI
|
||||||
import TelegramPresentationData
|
import TelegramPresentationData
|
||||||
import ChatPresentationInterfaceState
|
import ChatPresentationInterfaceState
|
||||||
|
import TextFormat
|
||||||
|
|
||||||
struct ChatInterfaceHighlightedState: Equatable {
|
struct ChatInterfaceHighlightedState: Equatable {
|
||||||
let messageStableId: UInt32
|
let messageStableId: UInt32
|
||||||
@ -74,6 +75,7 @@ public final class ChatControllerInteraction {
|
|||||||
let sendCurrentMessage: (Bool) -> Void
|
let sendCurrentMessage: (Bool) -> Void
|
||||||
let sendMessage: (String) -> Void
|
let sendMessage: (String) -> Void
|
||||||
let sendSticker: (FileMediaReference, Bool, Bool, String?, Bool, UIView, CGRect, CALayer?) -> Bool
|
let sendSticker: (FileMediaReference, Bool, Bool, String?, Bool, UIView, CGRect, CALayer?) -> Bool
|
||||||
|
let sendEmoji: (String, ChatTextInputTextCustomEmojiAttribute) -> Void
|
||||||
let sendGif: (FileMediaReference, UIView, CGRect, Bool, Bool) -> Bool
|
let sendGif: (FileMediaReference, UIView, CGRect, Bool, Bool) -> Bool
|
||||||
let sendBotContextResultAsGif: (ChatContextResultCollection, ChatContextResult, UIView, CGRect, Bool) -> Bool
|
let sendBotContextResultAsGif: (ChatContextResultCollection, ChatContextResult, UIView, CGRect, Bool) -> Bool
|
||||||
let requestMessageActionCallback: (MessageId, MemoryBuffer?, Bool, Bool) -> Void
|
let requestMessageActionCallback: (MessageId, MemoryBuffer?, Bool, Bool) -> Void
|
||||||
@ -179,6 +181,7 @@ public final class ChatControllerInteraction {
|
|||||||
sendCurrentMessage: @escaping (Bool) -> Void,
|
sendCurrentMessage: @escaping (Bool) -> Void,
|
||||||
sendMessage: @escaping (String) -> Void,
|
sendMessage: @escaping (String) -> Void,
|
||||||
sendSticker: @escaping (FileMediaReference, Bool, Bool, String?, Bool, UIView, CGRect, CALayer?) -> Bool,
|
sendSticker: @escaping (FileMediaReference, Bool, Bool, String?, Bool, UIView, CGRect, CALayer?) -> Bool,
|
||||||
|
sendEmoji: @escaping (String, ChatTextInputTextCustomEmojiAttribute) -> Void,
|
||||||
sendGif: @escaping (FileMediaReference, UIView, CGRect, Bool, Bool) -> Bool,
|
sendGif: @escaping (FileMediaReference, UIView, CGRect, Bool, Bool) -> Bool,
|
||||||
sendBotContextResultAsGif: @escaping (ChatContextResultCollection, ChatContextResult, UIView, CGRect, Bool) -> Bool,
|
sendBotContextResultAsGif: @escaping (ChatContextResultCollection, ChatContextResult, UIView, CGRect, Bool) -> Bool,
|
||||||
requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool, Bool) -> Void,
|
requestMessageActionCallback: @escaping (MessageId, MemoryBuffer?, Bool, Bool) -> Void,
|
||||||
@ -267,6 +270,7 @@ public final class ChatControllerInteraction {
|
|||||||
self.sendCurrentMessage = sendCurrentMessage
|
self.sendCurrentMessage = sendCurrentMessage
|
||||||
self.sendMessage = sendMessage
|
self.sendMessage = sendMessage
|
||||||
self.sendSticker = sendSticker
|
self.sendSticker = sendSticker
|
||||||
|
self.sendEmoji = sendEmoji
|
||||||
self.sendGif = sendGif
|
self.sendGif = sendGif
|
||||||
self.sendBotContextResultAsGif = sendBotContextResultAsGif
|
self.sendBotContextResultAsGif = sendBotContextResultAsGif
|
||||||
self.requestMessageActionCallback = requestMessageActionCallback
|
self.requestMessageActionCallback = requestMessageActionCallback
|
||||||
|
@ -2563,6 +2563,15 @@ class ChatControllerNode: ASDisplayNode, UIScrollViewDelegate {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func frameForEmojiButton() -> CGRect? {
|
||||||
|
if let textInputPanelNode = self.textInputPanelNode, self.inputPanelNode === textInputPanelNode {
|
||||||
|
return textInputPanelNode.frameForEmojiButton().flatMap {
|
||||||
|
return $0.offsetBy(dx: textInputPanelNode.frame.minX, dy: textInputPanelNode.frame.minY)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
var isTextInputPanelActive: Bool {
|
var isTextInputPanelActive: Bool {
|
||||||
return self.inputPanelNode is ChatTextInputPanelNode
|
return self.inputPanelNode is ChatTextInputPanelNode
|
||||||
}
|
}
|
||||||
|
@ -225,7 +225,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
|||||||
}, callPeer: { peerId, isVideo in
|
}, callPeer: { peerId, isVideo in
|
||||||
self?.controllerInteraction?.callPeer(peerId, isVideo)
|
self?.controllerInteraction?.callPeer(peerId, isVideo)
|
||||||
}, enqueueMessage: { _ in
|
}, enqueueMessage: { _ in
|
||||||
}, sendSticker: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { signal, media in
|
}, sendSticker: nil, sendEmoji: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { signal, media in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.temporaryHiddenGalleryMediaDisposable.set((signal |> deliverOnMainQueue).start(next: { messageId in
|
strongSelf.temporaryHiddenGalleryMediaDisposable.set((signal |> deliverOnMainQueue).start(next: { messageId in
|
||||||
if let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction {
|
if let strongSelf = self, let controllerInteraction = strongSelf.controllerInteraction {
|
||||||
@ -261,7 +261,7 @@ final class ChatRecentActionsControllerNode: ViewControllerTracingNode {
|
|||||||
}, activateMessagePinch: { _ in
|
}, activateMessagePinch: { _ in
|
||||||
}, openMessageContextActions: { _, _, _, _ in
|
}, openMessageContextActions: { _, _, _, _ in
|
||||||
}, navigateToMessage: { _, _ in }, navigateToMessageStandalone: { _ in
|
}, navigateToMessage: { _, _ in }, navigateToMessageStandalone: { _ in
|
||||||
}, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _, _, _, _, _ in return false }, sendGif: { _, _, _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _, _ in return false }, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { [weak self] url, _, _, _ in
|
}, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _, _, _, _, _ in return false }, sendEmoji: { _, _ in }, sendGif: { _, _, _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _, _ in return false }, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { [weak self] url, _, _, _ in
|
||||||
self?.openUrl(url)
|
self?.openUrl(url)
|
||||||
}, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { [weak self] message, associatedData in
|
}, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { [weak self] message, associatedData in
|
||||||
if let strongSelf = self, let navigationController = strongSelf.getNavigationController() {
|
if let strongSelf = self, let navigationController = strongSelf.getNavigationController() {
|
||||||
|
@ -3468,6 +3468,15 @@ class ChatTextInputPanelNode: ChatInputPanelNode, ASEditableTextNodeDelegate {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func frameForEmojiButton() -> CGRect? {
|
||||||
|
for (item, button) in self.accessoryItemButtons {
|
||||||
|
if case let .input(_, inputMode) = item, case .emoji = inputMode {
|
||||||
|
return button.frame.insetBy(dx: 0.0, dy: 6.0)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
func makeSnapshotForTransition() -> ChatMessageTransitionNode.Source.TextInput? {
|
func makeSnapshotForTransition() -> ChatMessageTransitionNode.Source.TextInput? {
|
||||||
guard let backgroundImage = self.transparentTextInputBackgroundImage else {
|
guard let backgroundImage = self.transparentTextInputBackgroundImage else {
|
||||||
return nil
|
return nil
|
||||||
|
@ -112,7 +112,7 @@ private final class DrawingStickersScreenNode: ViewControllerTracingNode {
|
|||||||
return false }, openPeer: { _, _, _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _, _ in }, openMessageReactionContextMenu: { _, _, _, _ in
|
return false }, openPeer: { _, _, _, _, _ in }, openPeerMention: { _ in }, openMessageContextMenu: { _, _, _, _, _, _ in }, openMessageReactionContextMenu: { _, _, _, _ in
|
||||||
}, updateMessageReaction: { _, _ in }, activateMessagePinch: { _ in
|
}, updateMessageReaction: { _, _ in }, activateMessagePinch: { _ in
|
||||||
}, openMessageContextActions: { _, _, _, _ in }, navigateToMessage: { _, _ in }, navigateToMessageStandalone: { _ in
|
}, openMessageContextActions: { _, _, _, _ in }, navigateToMessage: { _, _ in }, navigateToMessageStandalone: { _ in
|
||||||
}, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { fileReference, _, _, _, _, node, rect, _ in return selectStickerImpl?(fileReference, node, rect) ?? false }, sendGif: { _, _, _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _, _ in return false }, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
|
}, tapMessage: nil, clickThroughMessage: { }, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { fileReference, _, _, _, _, node, rect, _ in return selectStickerImpl?(fileReference, node, rect) ?? false }, sendEmoji: { _, _ in }, sendGif: { _, _, _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _, _ in return false }, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
|
||||||
}, presentController: { _, _ in
|
}, presentController: { _, _ in
|
||||||
}, presentControllerInCurrent: { _, _ in
|
}, presentControllerInCurrent: { _, _ in
|
||||||
}, navigationController: {
|
}, navigationController: {
|
||||||
|
@ -78,7 +78,7 @@ func openChatMessageImpl(_ params: OpenChatMessageParams) -> Bool {
|
|||||||
params.navigationController?.pushViewController(controller)
|
params.navigationController?.pushViewController(controller)
|
||||||
return true
|
return true
|
||||||
case let .stickerPack(reference):
|
case let .stickerPack(reference):
|
||||||
let controller = StickerPackScreen(context: params.context, updatedPresentationData: params.updatedPresentationData, mainStickerPack: reference, stickerPacks: [reference], parentNavigationController: params.navigationController, sendSticker: params.sendSticker, actionPerformed: { actions in
|
let controller = StickerPackScreen(context: params.context, updatedPresentationData: params.updatedPresentationData, mainStickerPack: reference, stickerPacks: [reference], parentNavigationController: params.navigationController, sendSticker: params.sendSticker, sendEmoji: params.sendEmoji, actionPerformed: { actions in
|
||||||
let presentationData = params.context.sharedContext.currentPresentationData.with { $0 }
|
let presentationData = params.context.sharedContext.currentPresentationData.with { $0 }
|
||||||
|
|
||||||
if actions.count > 1, let first = actions.first {
|
if actions.count > 1, let first = actions.first {
|
||||||
|
@ -82,6 +82,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
|
|||||||
}, sendMessage: { _ in
|
}, sendMessage: { _ in
|
||||||
}, sendSticker: { _, _, _, _, _, _, _, _ in
|
}, sendSticker: { _, _, _, _, _, _, _, _ in
|
||||||
return false
|
return false
|
||||||
|
}, sendEmoji: { _, _ in
|
||||||
}, sendGif: { _, _, _, _, _ in
|
}, sendGif: { _, _, _, _, _ in
|
||||||
return false
|
return false
|
||||||
}, sendBotContextResultAsGif: { _, _, _, _, _ in
|
}, sendBotContextResultAsGif: { _, _, _, _, _ in
|
||||||
@ -282,7 +283,7 @@ final class OverlayAudioPlayerControllerNode: ViewControllerTracingNode, UIGestu
|
|||||||
if let location = strongSelf.playlistLocation as? PeerMessagesPlaylistLocation, case let .custom(messages, _, loadMore) = location {
|
if let location = strongSelf.playlistLocation as? PeerMessagesPlaylistLocation, case let .custom(messages, _, loadMore) = location {
|
||||||
playlistLocation = .custom(messages: messages, at: id, loadMore: loadMore)
|
playlistLocation = .custom(messages: messages, at: id, loadMore: loadMore)
|
||||||
}
|
}
|
||||||
return strongSelf.context.sharedContext.openChatMessage(OpenChatMessageParams(context: strongSelf.context, chatLocation: nil, chatLocationContextHolder: nil, message: message, standalone: false, reverseMessageGalleryOrder: false, navigationController: nil, dismissInput: { }, present: { _, _ in }, transitionNode: { _, _ in return nil }, addToTransitionSurface: { _ in }, openUrl: { _ in }, openPeer: { _, _ in }, callPeer: { _, _ in }, enqueueMessage: { _ in }, sendSticker: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in }, playlistLocation: playlistLocation))
|
return strongSelf.context.sharedContext.openChatMessage(OpenChatMessageParams(context: strongSelf.context, chatLocation: nil, chatLocationContextHolder: nil, message: message, standalone: false, reverseMessageGalleryOrder: false, navigationController: nil, dismissInput: { }, present: { _, _ in }, transitionNode: { _, _ in return nil }, addToTransitionSurface: { _ in }, openUrl: { _ in }, openPeer: { _, _ in }, callPeer: { _, _ in }, enqueueMessage: { _ in }, sendSticker: nil, sendEmoji: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in }, playlistLocation: playlistLocation))
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -1759,8 +1759,8 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
private let archivedPacks = Promise<[ArchivedStickerPackItem]?>()
|
private let archivedPacks = Promise<[ArchivedStickerPackItem]?>()
|
||||||
private let blockedPeers = Promise<BlockedPeersContext?>(nil)
|
private let blockedPeers = Promise<BlockedPeersContext?>(nil)
|
||||||
private let hasTwoStepAuth = Promise<Bool?>(nil)
|
private let hasTwoStepAuth = Promise<Bool?>(nil)
|
||||||
private let twoStepAuthData = Promise<TwoStepVerificationAccessConfiguration?>(nil)
|
private let twoStepAccessConfiguration = Promise<TwoStepVerificationAccessConfiguration?>(nil)
|
||||||
private let hasPassport = Promise<Bool>(false)
|
private let twoStepAuthData = Promise<TwoStepAuthData?>(nil)
|
||||||
private let supportPeerDisposable = MetaDisposable()
|
private let supportPeerDisposable = MetaDisposable()
|
||||||
private let tipsPeerDisposable = MetaDisposable()
|
private let tipsPeerDisposable = MetaDisposable()
|
||||||
private let cachedFaq = Promise<ResolvedUrl?>(nil)
|
private let cachedFaq = Promise<ResolvedUrl?>(nil)
|
||||||
@ -2271,6 +2271,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
}, sendMessage: { _ in
|
}, sendMessage: { _ in
|
||||||
}, sendSticker: { _, _, _, _, _, _, _, _ in
|
}, sendSticker: { _, _, _, _, _, _, _, _ in
|
||||||
return false
|
return false
|
||||||
|
}, sendEmoji: { _, _ in
|
||||||
}, sendGif: { _, _, _, _, _ in
|
}, sendGif: { _, _, _, _, _ in
|
||||||
return false
|
return false
|
||||||
}, sendBotContextResultAsGif: { _, _, _, _, _ in
|
}, sendBotContextResultAsGif: { _, _, _, _, _ in
|
||||||
@ -3030,21 +3031,28 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
))
|
))
|
||||||
self.privacySettings.set(.single(nil) |> then(context.engine.privacy.requestAccountPrivacySettings() |> map(Optional.init)))
|
self.privacySettings.set(.single(nil) |> then(context.engine.privacy.requestAccountPrivacySettings() |> map(Optional.init)))
|
||||||
self.archivedPacks.set(.single(nil) |> then(context.engine.stickers.archivedStickerPacks() |> map(Optional.init)))
|
self.archivedPacks.set(.single(nil) |> then(context.engine.stickers.archivedStickerPacks() |> map(Optional.init)))
|
||||||
self.twoStepAuthData.set(.single(nil) |> then(context.engine.auth.twoStepVerificationConfiguration()
|
self.twoStepAccessConfiguration.set(.single(nil) |> then(context.engine.auth.twoStepVerificationConfiguration()
|
||||||
|> map { value -> TwoStepVerificationAccessConfiguration? in
|
|> map { value -> TwoStepVerificationAccessConfiguration? in
|
||||||
return TwoStepVerificationAccessConfiguration(configuration: value, password: nil)
|
return TwoStepVerificationAccessConfiguration(configuration: value, password: nil)
|
||||||
}))
|
}))
|
||||||
self.hasPassport.set(.single(false) |> then(context.engine.auth.twoStepAuthData()
|
|
||||||
|> map { value -> Bool in
|
self.twoStepAuthData.set(.single(nil)
|
||||||
return value.hasSecretValues
|
|> then(
|
||||||
|
context.engine.auth.twoStepAuthData()
|
||||||
|
|> map(Optional.init)
|
||||||
|
|> `catch` { _ -> Signal<TwoStepAuthData?, NoError> in
|
||||||
|
return .single(nil)
|
||||||
|
}
|
||||||
|
))
|
||||||
|
|
||||||
|
let hasPassport = self.twoStepAuthData.get()
|
||||||
|
|> map { data -> Bool in
|
||||||
|
return data?.hasSecretValues ?? false
|
||||||
}
|
}
|
||||||
|> `catch` { _ -> Signal<Bool, NoError> in
|
|
||||||
return .single(false)
|
|
||||||
}))
|
|
||||||
|
|
||||||
self.cachedFaq.set(.single(nil) |> then(cachedFaqInstantPage(context: self.context) |> map(Optional.init)))
|
self.cachedFaq.set(.single(nil) |> then(cachedFaqInstantPage(context: self.context) |> map(Optional.init)))
|
||||||
|
|
||||||
screenData = peerInfoScreenSettingsData(context: context, peerId: peerId, accountsAndPeers: self.accountsAndPeers.get(), activeSessionsContextAndCount: self.activeSessionsContextAndCount.get(), notificationExceptions: self.notificationExceptions.get(), privacySettings: self.privacySettings.get(), archivedStickerPacks: self.archivedPacks.get(), hasPassport: self.hasPassport.get())
|
screenData = peerInfoScreenSettingsData(context: context, peerId: peerId, accountsAndPeers: self.accountsAndPeers.get(), activeSessionsContextAndCount: self.activeSessionsContextAndCount.get(), notificationExceptions: self.notificationExceptions.get(), privacySettings: self.privacySettings.get(), archivedStickerPacks: self.archivedPacks.get(), hasPassport: hasPassport)
|
||||||
|
|
||||||
|
|
||||||
self.headerNode.displayCopyContextMenu = { [weak self] node, copyPhone, copyUsername in
|
self.headerNode.displayCopyContextMenu = { [weak self] node, copyPhone, copyUsername in
|
||||||
@ -3404,7 +3412,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
}, callPeer: { peerId, isVideo in
|
}, callPeer: { peerId, isVideo in
|
||||||
//self?.controllerInteraction?.callPeer(peerId)
|
//self?.controllerInteraction?.callPeer(peerId)
|
||||||
}, enqueueMessage: { _ in
|
}, enqueueMessage: { _ in
|
||||||
}, sendSticker: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in }, actionInteraction: GalleryControllerActionInteraction(openUrl: { [weak self] url, concealed in
|
}, sendSticker: nil, sendEmoji: nil, setupTemporaryHiddenMedia: { _, _, _ in }, chatAvatarHiddenMedia: { _, _ in }, actionInteraction: GalleryControllerActionInteraction(openUrl: { [weak self] url, concealed in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
strongSelf.openUrl(url: url, concealed: false, external: false)
|
strongSelf.openUrl(url: url, concealed: false, external: false)
|
||||||
}
|
}
|
||||||
@ -6369,13 +6377,16 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
|> take(1)
|
|> take(1)
|
||||||
|> deliverOnMainQueue).start(next: { [weak self] blockedPeersContext, hasTwoStepAuth in
|
|> deliverOnMainQueue).start(next: { [weak self] blockedPeersContext, hasTwoStepAuth in
|
||||||
if let strongSelf = self {
|
if let strongSelf = self {
|
||||||
|
let loginEmailPattern = strongSelf.twoStepAuthData.get() |> map { data -> String? in
|
||||||
|
return data?.loginEmailPattern
|
||||||
|
}
|
||||||
push(privacyAndSecurityController(context: strongSelf.context, initialSettings: settings.privacySettings, updatedSettings: { [weak self] settings in
|
push(privacyAndSecurityController(context: strongSelf.context, initialSettings: settings.privacySettings, updatedSettings: { [weak self] settings in
|
||||||
self?.privacySettings.set(.single(settings))
|
self?.privacySettings.set(.single(settings))
|
||||||
}, updatedBlockedPeers: { [weak self] blockedPeersContext in
|
}, updatedBlockedPeers: { [weak self] blockedPeersContext in
|
||||||
self?.blockedPeers.set(.single(blockedPeersContext))
|
self?.blockedPeers.set(.single(blockedPeersContext))
|
||||||
}, updatedHasTwoStepAuth: { [weak self] hasTwoStepAuthValue in
|
}, updatedHasTwoStepAuth: { [weak self] hasTwoStepAuthValue in
|
||||||
self?.hasTwoStepAuth.set(.single(hasTwoStepAuthValue))
|
self?.hasTwoStepAuth.set(.single(hasTwoStepAuthValue))
|
||||||
}, focusOnItemTag: nil, activeSessionsContext: settings.activeSessionsContext, webSessionsContext: settings.webSessionsContext, blockedPeersContext: blockedPeersContext, hasTwoStepAuth: hasTwoStepAuth))
|
}, focusOnItemTag: nil, activeSessionsContext: settings.activeSessionsContext, webSessionsContext: settings.webSessionsContext, blockedPeersContext: blockedPeersContext, hasTwoStepAuth: hasTwoStepAuth, loginEmailPattern: loginEmailPattern))
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
@ -6883,7 +6894,7 @@ final class PeerInfoScreenNode: ViewControllerTracingNode, UIScrollViewDelegate
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
}, resolvedFaqUrl: self.cachedFaq.get(), exceptionsList: .single(settings.notificationExceptions), archivedStickerPacks: .single(settings.archivedStickerPacks), privacySettings: .single(settings.privacySettings), hasTwoStepAuth: self.hasTwoStepAuth.get(), twoStepAuthData: self.twoStepAuthData.get(), activeSessionsContext: self.activeSessionsContextAndCount.get() |> map { $0?.0 }, webSessionsContext: self.activeSessionsContextAndCount.get() |> map { $0?.2 }), cancel: { [weak self] in
|
}, resolvedFaqUrl: self.cachedFaq.get(), exceptionsList: .single(settings.notificationExceptions), archivedStickerPacks: .single(settings.archivedStickerPacks), privacySettings: .single(settings.privacySettings), hasTwoStepAuth: self.hasTwoStepAuth.get(), twoStepAuthData: self.twoStepAccessConfiguration.get(), activeSessionsContext: self.activeSessionsContextAndCount.get() |> map { $0?.0 }, webSessionsContext: self.activeSessionsContextAndCount.get() |> map { $0?.2 }), cancel: { [weak self] in
|
||||||
self?.deactivateSearch()
|
self?.deactivateSearch()
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1281,7 +1281,7 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
tapMessage?(message)
|
tapMessage?(message)
|
||||||
}, clickThroughMessage: {
|
}, clickThroughMessage: {
|
||||||
clickThroughMessage?()
|
clickThroughMessage?()
|
||||||
}, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _, _, _, _, _ in return false }, sendGif: { _, _, _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _, _ in
|
}, toggleMessagesSelection: { _, _ in }, sendCurrentMessage: { _ in }, sendMessage: { _ in }, sendSticker: { _, _, _, _, _, _, _, _ in return false }, sendEmoji: { _, _ in }, sendGif: { _, _, _, _, _ in return false }, sendBotContextResultAsGif: { _, _, _, _, _ in
|
||||||
return false
|
return false
|
||||||
}, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
|
}, requestMessageActionCallback: { _, _, _, _ in }, requestMessageActionUrlAuth: { _, _ in }, activateSwitchInline: { _, _ in }, openUrl: { _, _, _, _ in }, shareCurrentLocation: {}, shareAccountContact: {}, sendBotCommand: { _, _ in }, openInstantPage: { _, _ in }, openWallpaper: { _ in }, openTheme: { _ in }, openHashtag: { _, _ in }, updateInputState: { _ in }, updateInputMode: { _ in }, openMessageShareMenu: { _ in
|
||||||
}, presentController: { _, _ in
|
}, presentController: { _, _ in
|
||||||
@ -1510,6 +1510,10 @@ public final class SharedAccountContextImpl: SharedAccountContext {
|
|||||||
public func makeStickerPackScreen(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, mainStickerPack: StickerPackReference, stickerPacks: [StickerPackReference], parentNavigationController: NavigationController?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?) -> ViewController {
|
public func makeStickerPackScreen(context: AccountContext, updatedPresentationData: (initial: PresentationData, signal: Signal<PresentationData, NoError>)?, mainStickerPack: StickerPackReference, stickerPacks: [StickerPackReference], parentNavigationController: NavigationController?, sendSticker: ((FileMediaReference, UIView, CGRect) -> Bool)?) -> ViewController {
|
||||||
return StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: mainStickerPack, stickerPacks: stickerPacks, parentNavigationController: parentNavigationController, sendSticker: sendSticker)
|
return StickerPackScreen(context: context, updatedPresentationData: updatedPresentationData, mainStickerPack: mainStickerPack, stickerPacks: stickerPacks, parentNavigationController: parentNavigationController, sendSticker: sendSticker)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func makeProxySettingsController(sharedContext: SharedAccountContext, account: UnauthorizedAccount) -> ViewController {
|
||||||
|
return proxySettingsController(accountManager: sharedContext.accountManager, postbox: account.postbox, network: account.network, mode: .modal, presentationData: sharedContext.currentPresentationData.with { $0 }, updatedPresentationData: sharedContext.presentationData)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func peerInfoControllerImpl(context: AccountContext, updatedPresentationData: (PresentationData, Signal<PresentationData, NoError>)?, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, requestsContext: PeerInvitationImportersContext? = nil) -> ViewController? {
|
private func peerInfoControllerImpl(context: AccountContext, updatedPresentationData: (PresentationData, Signal<PresentationData, NoError>)?, peer: Peer, mode: PeerInfoControllerMode, avatarInitiallyExpanded: Bool, isOpenedFromChat: Bool, requestsContext: PeerInvitationImportersContext? = nil) -> ViewController? {
|
||||||
|
Loading…
x
Reference in New Issue
Block a user